From 1352ef8ec8e60169925fc4dd356df0f48a5b9aed Mon Sep 17 00:00:00 2001 From: sky <3324298144@qq.com> Date: Fri, 6 Sep 2024 01:19:05 +0800 Subject: [PATCH] init --- .gitignore | 1 + Cargo.lock | 23 +++++++++++ Cargo.toml | 8 ++++ src/decrypto/cipher.rs | 93 ++++++++++++++++++++++++++++++++++++++++++ src/decrypto/mod.rs | 2 + src/decrypto/rc4.rs | 62 ++++++++++++++++++++++++++++ src/key/mod.rs | 91 +++++++++++++++++++++++++++++++++++++++++ src/key/tea.rs | 51 +++++++++++++++++++++++ src/lib.rs | 10 +++++ 9 files changed, 341 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/decrypto/cipher.rs create mode 100644 src/decrypto/mod.rs create mode 100644 src/decrypto/rc4.rs create mode 100644 src/key/mod.rs create mode 100644 src/key/tea.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..115668e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "reuqm" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5740236 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "reuqm" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.86" +base64 = "0.22.1" diff --git a/src/decrypto/cipher.rs b/src/decrypto/cipher.rs new file mode 100644 index 0000000..1462b1d --- /dev/null +++ b/src/decrypto/cipher.rs @@ -0,0 +1,93 @@ +use std::cmp::min; + +use crate::{decrypto::rc4, key::read_key}; +use anyhow::{Error, Result}; + +const FIRST_SEGMENT_SIZE: usize = 128; +const OTHER_SEGMENT_SIZE: usize = 5120; + +pub struct Cipher { + hash: f64, + key: Vec, + key_stream: Vec, +} + +impl Cipher { + pub fn new(raw_key: &str) -> Result { + let key = read_key(raw_key)?; + let key_stream = rc4::new(&key); + let hash = hash(&key); + Ok(Self { + hash, + key, + key_stream, + }) + } + pub fn decrypt(&mut self, mut offset: usize, mut buffer: &mut [u8]) { + if offset < FIRST_SEGMENT_SIZE { + let n = min(FIRST_SEGMENT_SIZE - offset, buffer.len()); + let (block, rest) = buffer.split_at_mut(n); + buffer = rest; + self.decrypt_first_block(offset, block); + offset += n; + } + + match offset % OTHER_SEGMENT_SIZE { + 0 => {} + excess => { + let n = min(OTHER_SEGMENT_SIZE - excess, buffer.len()); + let (block, rest) = buffer.split_at_mut(n); + buffer = rest; + self.decrypt_other_block(offset, block); + offset += n; + } + }; + + while !buffer.is_empty() { + let n: usize = min(OTHER_SEGMENT_SIZE, buffer.len()); + let (block, rest) = buffer.split_at_mut(n); + buffer = rest; + self.decrypt_other_block(offset, block); + offset += n; + } + } + fn decrypt_first_block(&mut self, offset: usize, dst: &mut [u8]) { + let n = self.key.len(); + + for (value, offset) in dst.iter_mut().zip(offset..) { + *value ^= self.key[idx(offset as u64, self.key[offset % n], self.hash) as usize % n]; + } + } + fn decrypt_other_block(&mut self, offset: usize, dst: &mut [u8]) { + let n = self.key.len(); + let id = offset / OTHER_SEGMENT_SIZE; + let block_offset = offset % OTHER_SEGMENT_SIZE; + let seed = self.key[id % n]; + let skip = idx(id as u64, seed, self.hash) % 512; + let key_stream = self.key_stream.iter().skip(skip as usize + block_offset); + for (datum, &key) in dst.iter_mut().zip(key_stream) { + *datum ^= key; + } + } +} + +fn hash(key: &Vec) -> f64 { + let mut hash = 1u32; + for v in key.iter() { + if *v == 0 { + continue; + } + let next_hash = hash.wrapping_mul(*v as u32); + if next_hash == 0 || next_hash <= hash { + break; + } + hash = next_hash; + } + hash.into() +} +fn idx(id: u64, seed: u8, hash: f64) -> u32 { + match seed { + 0 => 0, + seed => (hash / ((id + 1).wrapping_mul(seed.into()) as f64) * 100.0) as u32, + } +} diff --git a/src/decrypto/mod.rs b/src/decrypto/mod.rs new file mode 100644 index 0000000..fbb2cb5 --- /dev/null +++ b/src/decrypto/mod.rs @@ -0,0 +1,2 @@ +pub mod cipher; +mod rc4; diff --git a/src/decrypto/rc4.rs b/src/decrypto/rc4.rs new file mode 100644 index 0000000..5b7ffdf --- /dev/null +++ b/src/decrypto/rc4.rs @@ -0,0 +1,62 @@ +pub fn new(key: &Vec) -> Vec { + let key_lenght = key.len(); + // init + let mut s_box = Vec::new(); + let mut t_box = Vec::new(); + for i in 0..key_lenght { + s_box.push(i as u8); + t_box.push(key[i % key_lenght]); + } + + // init S box + let mut j = 0; + for i in 0..key_lenght { + j = (j + s_box[i] as usize + t_box[i] as usize) % 512; + s_box.swap(i, j); + } + + let mut i = 0; + let mut j = 0; + let mut ks = Vec::new(); + for _ in 0..(5120 + 512) { + i = (i + 1) % key_lenght; + j = (j + s_box[i] as usize) % key_lenght; + s_box.swap(i, j); + let t = (s_box[i] as usize + s_box[j] as usize) % key_lenght; + ks.push(0 ^ s_box[t]); + } + ks +} + +#[test] +fn test() { + let k = vec![ + 102, 121, 54, 88, 65, 52, 108, 55, 49, 87, 86, 79, 104, 83, 52, 106, 107, 50, 113, 54, 83, + 82, 100, 99, 56, 103, 112, 78, 51, 56, 52, 112, 77, 53, 57, 56, 119, 49, 99, 117, 51, 76, + 120, 121, 49, 109, 83, 73, 49, 74, 56, 52, 55, 110, 99, 98, 77, 114, 112, 90, 72, 65, 82, + 48, 53, 50, 68, 48, 49, 108, 114, 97, 67, 82, 73, 83, 103, 116, 101, 53, 120, 121, 81, 73, + 89, 76, 53, 88, 100, 53, 53, 100, 116, 111, 97, 83, 119, 51, 104, 109, 48, 55, 75, 102, + 110, 66, 110, 76, 53, 49, 97, 102, 112, 76, 115, 104, 56, 113, 57, 89, 74, 81, 57, 103, 99, + 55, 77, 99, 51, 68, 50, 72, 48, 51, 86, 114, 121, 100, 75, 87, 66, 48, 54, 116, 107, 118, + 105, 76, 67, 65, 51, 68, 68, 51, 56, 103, 49, 52, 89, 87, 54, 76, 52, 81, 104, 66, 79, 52, + 112, 75, 49, 73, 114, 98, 68, 67, 84, 50, 50, 87, 74, 76, 104, 57, 102, 52, 119, 109, 116, + 51, 75, 71, 56, 65, 86, 80, 52, 53, 100, 71, 101, 69, 113, 81, 49, 75, 77, 54, 120, 102, + 85, 89, 121, 65, 101, 79, 101, 76, 74, 104, 99, 49, 75, 102, 55, 98, 55, 48, 71, 110, 98, + 48, 120, 57, 78, 120, 68, 73, 49, 52, 55, 103, 99, 89, 112, 97, 57, 88, 72, 51, 48, 75, 70, + 72, 106, 122, 72, 98, 100, 48, 113, 77, 56, 50, 108, 50, 65, 113, 69, 88, 102, 99, 70, 49, + 103, 54, 50, 103, 98, 90, 50, 112, 75, 116, 57, 103, 80, 97, 55, 54, 118, 57, 101, 57, 57, + 88, 57, 48, 49, 54, 115, 56, 52, 53, 79, 87, 66, 102, 116, 111, 54, 89, 51, 122, 102, 53, + 116, 50, 101, 56, 101, 102, 83, 111, 74, 81, 110, 50, 85, 56, 55, 51, 84, 81, 54, 51, 86, + 82, 51, 50, 72, 120, 56, 54, 115, 56, 117, 87, 122, 79, 56, 56, 105, 83, 53, 54, 82, 82, + 50, 103, 81, 119, 99, 56, 103, 54, 97, 83, 54, 107, 52, 102, 105, 68, 54, 108, 99, 66, 99, + 70, 122, 97, 101, 109, 72, 53, 105, 88, 55, 55, 111, 73, 50, 88, 114, 103, 82, 50, 99, 99, + 48, 57, 113, 106, 100, 72, 107, 109, 100, 54, 84, 51, 79, 106, 86, 97, 102, 118, 105, 76, + 56, 104, 107, 56, 53, 79, 67, 75, 50, 103, 53, 56, 112, 111, 117, 108, 54, 98, 110, 119, + 118, 118, 49, 80, 69, 119, 102, 101, 56, 51, 116, 71, 48, 86, 106, 104, 103, 71, 77, 72, + 52, 54, 117, 88, 67, 54, 86, 55, 48, 102, 67, 51, 49, 74, 57, 52, 119, 56, 54, 111, 119, + 108, 48, 79, 55, 72, 73, 57, 49, 72, 51, 81, 70, 74, 99, 55, 78, 53, 82, 84, 103, 52, 50, + 115, 112, 54, 54, 50, 52, 65, 114, 122, + ]; + let rs4 = new(&k); + println!("{:?}", rs4); +} diff --git a/src/key/mod.rs b/src/key/mod.rs new file mode 100644 index 0000000..9d84894 --- /dev/null +++ b/src/key/mod.rs @@ -0,0 +1,91 @@ +mod tea; +use anyhow::{Error, Result}; +use base64::prelude::{Engine, BASE64_STANDARD as base64}; + +pub fn read_key(raw_key: &str) -> Result> { + let base64_key = match base64.decode(raw_key) { + Ok(k) => k, + Err(err) => { + return Err(Error::msg(format!( + "use base64 decode raw key Failed,{err}" + ))) + } + }; + + let simple_key: [u8; 8] = [105, 86, 70, 56, 43, 32, 21, 11]; + let mut tea_key = vec![0; 16]; + for i in 0..8 { + tea_key[i << 1] = simple_key[i]; + tea_key[(i << 1) + 1] = base64_key[i]; + } + let mut rs = decrypt_tencent_tea(base64_key[8..].to_owned(), tea_key)?; + let mut dkv1 = base64_key[..8].to_owned(); + dkv1.append(&mut rs); + Ok(dkv1.into()) +} + +fn decrypt_tencent_tea(in_buf: Vec, key: Vec) -> Result, Error> { + const SALTLEN: usize = 2; + const ZEROEN: usize = 7; + if in_buf.len() % 8 != 0 { + return Err(Error::msg("inBuf size not a multiple of the block size")); + } + if in_buf.len() < 16 { + return Err(Error::msg("inBuf size too small")); + } + let key = key[0..16].try_into().unwrap(); + let blk = tea::Tea::new(key, 32); + let mut dst = blk.decrypt(in_buf[0..8].to_owned()); + + let pad_len = &dst[0] & 0x7; + let out_len = in_buf.len() - 1 - pad_len as usize - SALTLEN - ZEROEN; + let mut out = vec![0; out_len]; + let mut iv_prev = vec![0; 8]; + let mut iv_cur = in_buf[0..8].to_owned(); + let mut in_buf_pos = 8; + let mut dst_idx = 1 + pad_len; + let mut i = 1; + while i <= SALTLEN { + if dst_idx < 8 { + dst_idx += 1; + i += 1; + } else if dst_idx == 8 { + iv_prev = iv_cur.clone()[0..8].to_owned(); + iv_cur = in_buf[in_buf_pos..(in_buf_pos + 8)].to_owned(); + dst = xor_8bytes(dst.clone(), in_buf[in_buf_pos..(in_buf_pos + 8)].to_owned()); + dst = blk.decrypt(dst.clone()); + in_buf_pos += 8; + dst_idx = 0; + } + } + let mut out_pos = 0; + while out_pos < out_len { + if dst_idx < 8 { + out[out_pos] = dst[dst_idx as usize] ^ iv_prev[dst_idx as usize]; + dst_idx += 1; + out_pos += 1; + continue; + } + iv_prev = iv_cur.clone()[0..8].to_owned(); + iv_cur = in_buf[in_buf_pos..(in_buf_pos + 8)].to_owned(); + dst = xor_8bytes(dst.clone(), in_buf[in_buf_pos..(in_buf_pos + 8)].to_owned()); + dst = blk.decrypt(dst.clone()); + in_buf_pos += 8; + dst_idx = 0; + } + let mut i = 1; + while i <= ZEROEN { + i += 1; + if dst[dst_idx as usize] != iv_prev[dst_idx as usize] { + return Err(Error::msg("zero check failed")); + } + } + Ok(out) +} +pub fn xor_8bytes(a: Vec, b: Vec) -> Vec { + let mut dst = vec![0; 8]; + for i in 0..8 { + dst[i] = a[i] ^ b[i]; + } + dst +} diff --git a/src/key/tea.rs b/src/key/tea.rs new file mode 100644 index 0000000..cd4c66c --- /dev/null +++ b/src/key/tea.rs @@ -0,0 +1,51 @@ +pub struct Tea { + key: [u32; 4], + rounds: u32, +} + +impl Tea { + pub fn new(key: [u8; 16], rounds: u32) -> Self { + let key = [ + u32::from_be_bytes(key[0..4].try_into().unwrap()), + u32::from_be_bytes(key[4..8].try_into().unwrap()), + u32::from_be_bytes(key[8..12].try_into().unwrap()), + u32::from_be_bytes(key[12..16].try_into().unwrap()), + ]; + Self { key, rounds } + } + pub fn decrypt(&self, bl: Vec) -> Vec { + let delta = 0x9E3779b9; + let decrypto = |v: [u32; 2], k: [u32; 4]| -> (u32, u32) { + let mut sum: u32 = (self.rounds / 2).wrapping_mul(delta); + let mut v0 = v[0]; + let mut v1 = v[1]; + for _ in 0..(self.rounds / 2) { + v1 = v1.wrapping_sub( + ((v0 << 4).wrapping_add(k[2])) + ^ (v0.wrapping_add(sum)) + ^ ((v0 >> 5).wrapping_add(k[3])), + ); + v0 = v0.wrapping_sub( + ((v1 << 4).wrapping_add(k[0])) + ^ (v1.wrapping_add(sum)) + ^ ((v1 >> 5).wrapping_add(k[1])), + ); + sum = sum.wrapping_sub(delta); + } + (v0, v1) + }; + let v0 = u32::from_be_bytes(bl[0..4].try_into().unwrap()); + let v1 = u32::from_be_bytes(bl[4..8].try_into().unwrap()); + let (v0, v1) = decrypto([v0, v1], self.key); + [v0.to_be_bytes(), v1.to_be_bytes()].concat() + } +} + +#[test] +fn test() { + let v: [u8; 16] = [ + 33, 14, 26, 47, 81, 10, 19, 22, 47, 35, 88, 21, 45, 23, 44, 55, + ]; + let k1 = u32::from_be_bytes(v[0..4].try_into().unwrap()); + assert_eq!(k1, 554572335); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..616641c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,10 @@ +pub mod decrypto; +mod key; +use anyhow::{Error, Result}; + +pub fn decrypt(filename: &str, raw_key: &str) -> Result, Error> { + let mut audio = std::fs::read(filename)?; + let mut cipher = decrypto::cipher::Cipher::new(raw_key)?; + cipher.decrypt(0, &mut audio); + Ok(audio) +}