diff --git a/Cargo.lock b/Cargo.lock index 6c009d4..04b4668 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 4 +[[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 = "anstream" version = "0.6.20" @@ -52,12 +63,39 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +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 = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cc" version = "1.2.36" @@ -68,6 +106,22 @@ dependencies = [ "shlex", ] +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[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 = "clap" version = "4.5.47" @@ -114,6 +168,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.3.0" @@ -129,18 +192,58 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "find-msvc-tools" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -182,6 +285,12 @@ dependencies = [ "crc", ] +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + [[package]] name = "once_cell_polyfill" version = "1.70.1" @@ -206,6 +315,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -229,6 +349,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + [[package]] name = "unicode-ident" version = "1.0.19" @@ -239,9 +365,13 @@ checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" name = "unixtract" version = "0.1.0" dependencies = [ + "aes", + "cbc", "clap", "lz4", "lzma-rs", + "md5", + "sha1", ] [[package]] @@ -250,6 +380,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "windows-link" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index 1a85951..70418a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,8 @@ edition = "2024" [dependencies] clap = { version = "4.5", features = ["derive"] } lz4 = "1.23.1" -lzma-rs = "0.3" \ No newline at end of file +lzma-rs = "0.3" +aes = "0.8.4" +cbc = "0.1.2" +md5 = "0.7" +sha1 = "0.10" \ No newline at end of file diff --git a/src/formats/mod.rs b/src/formats/mod.rs index b0e487c..54855b4 100644 --- a/src/formats/mod.rs +++ b/src/formats/mod.rs @@ -1 +1,2 @@ -pub mod mstar; \ No newline at end of file +pub mod mstar; +pub mod samsung_old; \ No newline at end of file diff --git a/src/formats/mstar.rs b/src/formats/mstar.rs index 4fa9110..97340ae 100644 --- a/src/formats/mstar.rs +++ b/src/formats/mstar.rs @@ -3,13 +3,14 @@ use std::path::{Path}; use std::io::{Write, Cursor}; use lz4::block::decompress; use lzma_rs::lzma_decompress; + use crate::common; pub fn is_mstar_file(file: &File) -> bool { let header = common::read_file(&file, 0, 32768).expect("Failed to read from file."); let header_string = String::from_utf8_lossy(&header); - if header_string.contains("filepartload") & header_string.contains("MstarUpgrade") | header_string.contains("CtvUpgrade") | header_string.contains("LetvUpgrade"){ + if header_string.contains("filepartload"){ true } else { false @@ -40,7 +41,6 @@ fn decompress_lz4(compressed_data: &[u8], original_size: i32) -> Result, } pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box> { - println!(); let mut script = common::read_file(&file, 0, 32768)?; @@ -89,6 +89,7 @@ pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box Result<(), Box Result<(), Box {}.bin", offset, size, partname); + println!("Part - Offset: {}, Size: {} --> {}", offset, size, partname); if partname == "unknown"{ println!("- Unknown destination, skipping!"); @@ -151,8 +152,8 @@ pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box; + +use crate::common; + +use md5; + +pub fn is_samsung_old_dir(path: &PathBuf) -> bool { + if Path::new(&path).join("image").is_dir() & Path::new(&path).join("image/info.txt").exists(){ + true + } else { + false + } +} + +fn decrypt_aes(encrypted_data: &[u8], key: &[u8; 16], iv: &[u8; 16]) -> Result, Box> { + let mut data = encrypted_data.to_vec(); + let decryptor = Aes128CbcDec::new(key.into(), iv.into()); + let decrypted = decryptor.decrypt_padded_mut::(&mut data) + .map_err(|e| format!("!!Decryption error!!: {:?}", e))?; + + Ok(decrypted.to_vec()) +} + +fn decrypt_xor(data: &[u8], key: &str) -> Vec { + let key_bytes = key.as_bytes(); + data.iter() + .enumerate() + .map(|(i, &byte)| byte ^ key_bytes[i % key_bytes.len()]) + .collect() +} + +pub fn extract_samsung_old(path: &PathBuf, output_folder: &str) -> Result<(), Box> { + + let fw_info = fs::read_to_string(Path::new(&path).join("image/info.txt"))?; + println!("Firmware info: {}", fw_info); + + let image_path = Path::new(&path).join("image"); + let mut secret = ""; + let secret_t_ecp = [0x3E, 0xF6, 0x06, 0x72, 0x62, 0xCF, 0x0C, 0x67, 0x85, 0x98, 0xBF, 0xF2, 0x21, 0x69, 0xD1, 0xF1, 0xEA, 0x57, 0xC2, 0x84]; + + // from samygo tool + if fw_info.starts_with("T-GA"){ + secret = "SHWJUH:85a045ae-2296-484c-b457-ede832fcfbe1-646390a3-105e-40aa-85f6-da3086c70111"; + } else if fw_info.starts_with("T-MST5"){ + secret = "SHWJUH:eceb2c14-db11-425e-9ebf-5f9607f0eb4b-3c38193e-751e-4719-8884-9e76322c0cec"; + } else if fw_info.starts_with("B-FIRHT7"){ + secret = "d6442d-7b46b2f4-0f11-4623-af63-8bb0a0d54c80-a22fbe2c-1bb5-49cc-b194-25c0f2b870f4"; + } else if fw_info.starts_with("B-FIRU") | fw_info.starts_with("B-FIRH") | fw_info.starts_with("B-FIRBSP") | fw_info.starts_with("B-FIR2"){ + secret = "SHWJUH:db48ad51-c784-4f06-af57-1070a910c536-6a028bb5-e83e-45da-b326-a3a39ccba26c"; + } else if fw_info.starts_with("B-FIRB"){ + secret = "d6442d-7b46b2f4-0f11-4623-af63-8bb0a0d54c80-a22fbe2c-1bb5-49cc-b194-25c0f2b870f4"; + } else if fw_info.starts_with("T-MST10P"){ + secret = "b4c136-fbc93576-b3e8-4035-bf4e-ba4cb4ada1ac-f0d81cc4-8301-4832-bd60-f331295743ba"; + } else if fw_info.starts_with("B-ECB"){ + secret = "SHWJUH:8fb684a9-84c1-46cf-aa81-977bce241542-6db4c136-8540-4ee4-8704-d9cd18590d11"; + } else if fw_info.starts_with("T-VAL"){ + secret = "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00001abc2010"; + } else if fw_info.starts_with("T-TDT"){ + secret = "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00002abc2010"; + } else if fw_info.starts_with("T-MSX"){ + secret = "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00004abc2010"; + } else if fw_info.starts_with("T-CH"){ + secret = "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-611c4f8d4a71"; + } else if fw_info.starts_with("T-ECP") { + } else { + println!("Sorry, this firmware is not supported!"); + return Ok(()); + } + + for entry in fs::read_dir(image_path)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() { + if let Some(ext) = path.extension() { + if ext == "sec" { + let file = File::open(&path)?; + let filename = path.file_name().unwrap().to_str().unwrap(); + let file_size = file.metadata()?.len(); + println!("File: {}", filename); + let data = common::read_file(&file, 0, file_size.try_into().unwrap())?; + let salt = &data[8..16]; + + let key_md5; + let iv_md5; + + //Old -> key: md5 of (sha1 of secret as TEXT + salt) + // iv: md5 of (md5 of key + key) + + //2012 -> key: md5 of (secret + salt) + // iv: md5 of (md5 of key + key) + if fw_info.starts_with("T-ECP") { + let key: Vec = [&secret_t_ecp, salt].concat(); + key_md5 = md5::compute(&key); + + let mut iv = Vec::new(); + iv.extend_from_slice(&key_md5.0); + iv.extend_from_slice(&key); + iv_md5 = md5::compute(&iv); + } else { + let mut hasher = Sha1::new(); + hasher.update(secret); + let secret_digest = hasher.finalize(); + // yes it needs to be a string first then into bytes + let secret_digest_string = format!("{:x}", secret_digest); + let secret_digest_bytes = secret_digest_string.as_bytes(); + + let key: Vec = [&secret_digest_bytes, salt].concat(); + key_md5 = md5::compute(&key); + + let mut iv = Vec::new(); + iv.extend_from_slice(&key_md5.0); + iv.extend_from_slice(&key); + iv_md5 = md5::compute(&iv); + } + + //println!("Key: {:02x?}", key_md5); + //println!("IV: {:02x?}", iv_md5); + + let end = file_size - 260; + println!("- Decrypting file..."); + let decrypted_data = decrypt_aes(&data[16..end.try_into().unwrap()], &key_md5, &iv_md5)?; + + println!("- DeXORing file..."); + let xor_key = fw_info.split_whitespace().next().unwrap(); + let out_data = decrypt_xor(&decrypted_data, xor_key); + + let output_path = Path::new(&output_folder).join(filename.rsplit_once('.').map(|(left, _)| left).unwrap()); + + fs::create_dir_all(&output_folder)?; + let mut out_file = OpenOptions::new() + .append(true) + .create(true) + .open(output_path)?; + + out_file.write_all(&out_data)?; + } + } + } + } + + + Ok(()) +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 2414a89..81a2293 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,18 +22,24 @@ fn main() -> Result<(), Box> { let path = PathBuf::from(target_path); if path.is_dir() { - println!("Directory"); + if formats::samsung_old::is_samsung_old_dir(&path) { + println!("Samsung old firmware dir detected!"); + println!(); + formats::samsung_old::extract_samsung_old(&path, &output_path)? + } else { + println!("Input format not recognized!"); + } } else { - //println!("file"); let file = File::open(path)?; println!(); if formats::mstar::is_mstar_file(&file) { println!("Mstar upgrade file detected!"); + println!(); formats::mstar::extract_mstar(&file, &output_path)?; } else { - println!("File format not recognized!"); + println!("Input format not recognized!"); } }