use crate::apperror::AppError; use crate::messager; use aes::cipher::generic_array::typenum::U16; use aes::cipher::{generic_array::GenericArray, BlockDecrypt, KeyInit}; use aes::Aes128; use audiotags::{MimeType, Picture, Tag}; use base64::{self, Engine}; use colored::*; use log::{debug, info, trace}; use messager::Signals; use serde_derive::{Deserialize, Serialize}; use serde_json::{self, Value}; use std::fmt::Debug; use std::fs::{self, File}; use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::str::from_utf8; use std::vec; // lazy_static! { // // 解密需要的密钥 // static ref KEY_CORE: Vec = decode("687A4852416D736F356B496E62617857").unwrap();//绝对正确 // static ref KEY_META: Vec = decode("2331346C6A6B5F215C5D2630553C2728").unwrap(); // } // 原KEY_CORE数据:687A4852416D736F356B496E62617857 const NEW_KEY_CORE: [u8; 16] = [ 0x68, 0x7A, 0x48, 0x52, 0x41, 0x6D, 0x73, 0x6F, 0x35, 0x6B, 0x49, 0x6E, 0x62, 0x61, 0x78, 0x57, ]; // 原KEY_META数据:2331346C6A6B5F215C5D2630553C2728 const NEW_KEY_META: [u8; 16] = [ 0x23, 0x31, 0x34, 0x6C, 0x6A, 0x6B, 0x5F, 0x21, 0x5C, 0x5D, 0x26, 0x30, 0x55, 0x3C, 0x27, 0x28, ]; #[derive(Debug)] #[allow(unused_variables)] pub struct Ncmfile { /// 文件对象 pub file: File, /// 文件名称,不带文件后缀 pub filename: String, /// 文件名称,带后缀名 pub fullfilename: String, /// 文件大小 pub size: u64, /// 游标 pub position: u64, } impl Ncmfile { /// 各种工具方法 pub fn new(filepath: &str) -> Result { let file = match File::open(filepath) { Ok(f) => f, Err(_) => return Err(AppError::FileReadError), }; let path = Path::new(filepath); let fullfilename = path .file_name() .ok_or(AppError::FileReadError)? .to_str() .ok_or(AppError::FileReadError)? .to_string(); let size = file .metadata() .map_err(|_| AppError::CannotReadMetaInfo)? .len(); let filename = match Path::new(&filepath).file_stem() { Some(f) => f.to_str().ok_or(AppError::FileReadError)?.to_string(), None => return Err(AppError::CannotReadFileName), }; Ok(Ncmfile { file, filename, fullfilename, size, position: 0, }) } /// 根据传入的长度来读取文件 /// /// 该函数可以记录上次读取的位置,下次读取时从上次读取的位置开始 /// - length 想要读取的长度 pub fn seekread(&mut self, length: u64) -> Result, AppError> { if self.position + length > self.size { Err(AppError::FileReadError) } else { let mut reader = BufReader::new(&self.file); let _ = reader.seek(SeekFrom::Start(self.position)); let mut buf = vec![0; length as usize]; let _ = reader.read_exact(&mut buf); self.position += length; Ok(buf[..].to_vec()) } } /// 从指定位置开始读取。 /// /// !!!该函数仍然会更新游标 /// /// - offset 开始位置 /// - length 想要读取的长度 #[allow(dead_code)] pub fn seekread_from(&mut self, offset: u64, length: u64) -> Result, AppError> { if self.position + length > self.size { Err(AppError::FileReadError) } else { let mut reader = BufReader::new(&self.file); let _ = reader.seek(SeekFrom::Start(offset)); let mut buf = vec![0; length as usize]; let _ = reader.read_exact(&mut buf); self.position = offset + length; Ok(buf[..].to_vec()) } } #[allow(dead_code)] pub fn seekread_to_end(&mut self) -> Result, std::io::Error> { let mut reader = BufReader::new(&self.file); reader.seek(SeekFrom::Start(self.position))?; let mut buf = vec![0; self.size as usize - self.position as usize]; reader.read_exact(&mut buf)?; self.position = self.size; Ok(buf[..].to_vec()) } pub fn seekread_no_error(&mut self, length: u64) -> Vec { if self.position + length > self.size { if self.position >= self.size { vec![] } else { let mut reader = BufReader::new(&self.file); let _ = reader.seek(SeekFrom::Start(self.position)); let mut buf: Vec = vec![0; (self.size - self.position) as usize]; let _ = reader.read_exact(&mut buf); self.position += length; buf[..].to_vec() } } else { let mut reader = BufReader::new(&self.file); let _ = reader.seek(SeekFrom::Start(self.position)); let mut buf = vec![0; length as usize]; let _ = reader.read_exact(&mut buf); self.position += length; buf[..].to_vec() } } /// 跳过某些数据 pub fn skip(&mut self, length: u64) -> Result<(), AppError> { if self.position + length > self.size { Err(AppError::FileReadError) } else { self.position += length; Ok(()) } } ///按字节进行0x64异或。 fn parse_key(key: &mut [u8]) -> &[u8] { for item in &mut *key { *item ^= 0x64; } key } fn save(&mut self, path: &PathBuf, data: Vec) -> Result<(), AppError> { let music_file = match File::create(path) { Ok(o) => o, Err(_) => return Err(AppError::FileWriteError), }; let mut writer = BufWriter::new(music_file); let _ = writer.write_all(&data); // 关闭文件 match writer.flush() { Ok(o) => o, Err(_) => return Err(AppError::FileWriteError), }; Ok(()) } fn is_ncm(data: Vec) -> Result<(), AppError> { let header = from_utf8(&data).map_err(|_| AppError::NotNcmFile)?; if header != "CTENFDAM" { Err(AppError::NotNcmFile) } else { Ok(()) } } /// 使用PKCS5Padding标准,去掉填充信息 fn unpad(data: &[u8]) -> Vec { data[..data.len() - data[data.len() - 1] as usize].to_vec() } } impl Ncmfile { /// 解密函数 #[allow(unused_assignments)] pub fn dump( &mut self, outputdir: &Path, tx: crossbeam_channel::Sender, force_save: bool, ) -> Result<(), AppError> { let messager = messager::Messager::new(self.fullfilename.clone(), tx); let _ = messager.send(Signals::Start); // 获取magic header 应为CTENFDAM trace!("读取magic header"); let magic_header = self.seekread(8)?; // 判断是否为ncm格式的文件 trace!("判断是否为ncm格式的文件"); Self::is_ncm(magic_header)?; // 跳过2字节 trace!("跳过2字节"); self.skip(2)?; trace!("获取RC4密钥长度"); //小端模式读取RC4密钥长度 正常情况下应为128 let key_length = u32::from_le_bytes( self.seekread(4)? .try_into() .map_err(|_| AppError::FileReadError)?, ) as u64; //数据长度不够只能使用u32 然后转化为u64 // debug!("RC4密钥长度为:{}", key_length); //读取密钥 开头应为 neteasecloudmusic trace!("读取RC4密钥"); let mut key_data = self.seekread(key_length)?; //aes128解密 let key_data = &aes128_to_slice(&NEW_KEY_CORE, Self::parse_key(&mut key_data[..]).to_vec())?; //先把密钥按照字节进行0x64异或 // RC4密钥 let key_data = Self::unpad(&key_data[..])[17..].to_vec(); //去掉neteasecloudmusic //读取meta信息的数据大小 trace!("获取meta信息数据大小"); let meta_length = u32::from_le_bytes( self.seekread(4)? .try_into() .map_err(|_| AppError::FileDataError)?, ) as u64; let _ = messager.send(Signals::GetMetaInfo); // 读取meta信息 trace!("读取meta信息"); let meta_data = { let mut meta_data = self.seekread(meta_length)?; //读取源数据 //字节对0x63进行异或。 for item in &mut meta_data { *item ^= 0x63; } // base64解密 let mut decode_data = Vec::::new(); if base64::engine::general_purpose::STANDARD .decode_vec(&mut meta_data[22..], &mut decode_data) .is_err() { return Err(AppError::CannotReadMetaInfo); }; // aes128解密 let aes_data = aes128_to_slice(&NEW_KEY_META, decode_data)?; // unpadding let json_data = match String::from_utf8(Self::unpad(&aes_data)[6..].to_vec()) { Ok(o) => o, Err(_) => return Err(AppError::CannotReadMetaInfo), }; debug!("json_data: {}", json_data); let data: Value = match serde_json::from_str(&json_data[..]) { Ok(o) => o, Err(_) => return Err(AppError::CannotReadMetaInfo), }; //解析json数据 data }; //处理文件路径 trace!("拼接文件路径"); let path = { let filename = format!( "{}.{}", self.filename, meta_data .get("format") .ok_or(AppError::CannotReadMetaInfo)? .as_str() .ok_or(AppError::CannotReadMetaInfo)? ); // let filename = standardize_filename(filename); debug!("文件名:{}", filename.yellow()); //链级创建输出目录 if fs::create_dir_all(outputdir).is_err() { return Err(AppError::FileWriteError); } outputdir.join(filename) }; debug!("文件路径: {:?}", path); // 先检查是否存在 if !force_save && Path::new(&path).exists() { return Err(AppError::ProtectFile); } // 跳过4个字节的校验码 // trace!("读取校验码"); // let _crc32 = u32::from_le_bytes(self.seekread(4)?.try_into().map_err(AppError::FileDataError)?) as u64; self.skip(4)?; // 跳过5个字节 trace!("跳过5个字节"); self.skip(5)?; let _ = messager.send(Signals::GetCover); // 获取图片数据的大小 trace!("获取图片数据的大小"); let image_data_length = u32::from_le_bytes( self.seekread(4)? .try_into() .map_err(|_| AppError::FileDataError)?, ) as u64; // 读取图片,并写入文件当中 let image_data = self.seekread(image_data_length)?; //读取图片数据 trace!("组成密码盒"); let key_box = { let key_length = key_data.len(); // let key_data = Vec::from(key_data); let mut key_box = (0..=255).collect::>(); let mut temp = 0; let mut last_byte = 0; let mut key_offset = 0; for i in 0..=255 { let swap = key_box[i as usize] as u64; temp = (swap + last_byte + key_data[key_offset] as u64) & 0xFF; key_offset += 1; if key_offset >= key_length { key_offset = 0; } key_box[i as usize] = key_box[temp as usize]; key_box[temp as usize] = swap as u8; last_byte = temp; } // let key_box = key_box.clone(); key_box }; //解密音乐数据 trace!("解密音乐数据"); let _ = messager.send(Signals::Decrypt); let mut music_data: Vec = Vec::new(); loop { let mut chunk = self.seekread_no_error(0x8000); let chunk_length = chunk.len(); if chunk_length != 0 { for i in 1..chunk_length + 1 { let j = i & 0xFF; chunk[i - 1] ^= key_box[(key_box[j] as usize + key_box[(key_box[j] as usize + j) & 0xff] as usize) & 0xff] // chunk[i - 1] ^= key_box[(key_box[j] + key_box[(key_box[j as usize] as usize + j as usize) & 0xFF]) & 0xFF]; } //向music_data中最追加chunk music_data.append(&mut chunk); } else { break; } } //退出循环,写入文件 let _ = messager.send(Signals::Save); self.save(&path, music_data)?; { // 保存封面 let mut tag = match Tag::new().read_from_path(&path) { Ok(o) => o, Err(_) => return Err(AppError::CoverCannotSave), }; let cover = Picture { mime_type: MimeType::Jpeg, data: &image_data, }; tag.set_album_cover(cover); //添加封面 let _ = tag .write_to_path(path.to_str().ok_or(AppError::SaveError)?) .map_err(|_| AppError::SaveError); //保存 } info!( "[{}] 文件已保存到: {}", self.filename.yellow(), path.to_str().ok_or(AppError::SaveError)?.bright_cyan() ); info!( "[{}]{}", self.fullfilename.yellow(), "解密成功".bright_green() ); let _ = messager.send(Signals::End); Ok(()) } } /// 存储元数据的结构体 #[derive(Serialize, Deserialize, Debug)] #[allow(unused_variables, dead_code)] struct Metadata { //编号 #[serde(rename = "musicId", skip)] //没用过,跳过 music_id: String, // 音乐名称 #[serde(rename = "musicName")] music_name: String, // 艺术家 #[serde(rename = "artist")] music_artist: Vec<(String, String)>, // 专辑id #[serde(rename = "albumId")] album_id: String, // 专辑 #[serde(rename = "album")] album: String, // #[serde(rename = "albumPicDocId", skip)] album_pic_doc_id: String, // #[serde(rename = "albumPic", skip)] album_pic: String, // 比特率 #[serde(rename = "bitrate")] bitrate: u128, // #[serde(rename = "mp3DocId", skip)] mp3_doc_id: String, // 时间长短 #[serde(rename = "duration")] duration: u128, // #[serde(rename = "mvId")] mv_id: String, // 别名 #[serde(rename = "alias")] alias: Vec, // 译名 #[serde(rename = "transNames")] trans_names: Vec, // 音乐格式 #[serde(rename = "format")] format: String, } // 存储各种密钥的结构体 #[derive(Clone)] #[allow(dead_code)] pub struct Key { pub core: Vec, pub meta: Vec, } // fn read_meta(file: &mut File, meta_length: u32) -> Result, Error> {} fn convert_to_generic_arrays(input: &[u8]) -> Result>, AppError> { // 确保输入的长度是16的倍数 if input.len() % 16 != 0 { return Err(AppError::FileDataError); } Ok(input .chunks(16) .map(|chunk| { // 将每个块转换为GenericArray GenericArray::clone_from_slice(chunk) }) .collect()) } /// ## AES128解密 /// 解密NCM文件的rc4密钥前记得按字节对0x64进行异或 fn aes128_to_slice>(key: &T, blocks: Vec) -> Result, AppError> { trace!("进行AES128解密"); let key = GenericArray::from_slice(key.as_ref()); let mut blocks = convert_to_generic_arrays(&blocks)?; // 初始化密钥 let cipher = Aes128::new(key); // 开始解密 cipher.decrypt_blocks(&mut blocks); //取出解密后的值 let mut x: Vec = Vec::new(); for block in blocks.iter() { for i in block { x.push(i.to_owned()); } } Ok(x) } // ## 规范文件名称 // 防止创建文件失败 // 符号一一对应: // - \ / * ? " : < > | // - _ _ * ? " : ⟨ ⟩ _ // #[allow(dead_code)] // fn standardize_filename(old_fullfilename: String) -> String { // trace!("格式化文件名"); // let mut new_fullfilename = String::from(old_fullfilename); // // debug!("规范文件名:{}", new_fullfilename); // let standard = ["\\", "/", "*", "?", "\"", ":", "<", ">", "|"]; // let resolution = ["_", "_", "*", "?", """, ":", "⟨", "⟩", "_"]; // for i in 0..standard.len() { // new_fullfilename = // new_fullfilename.replace(&standard[i].to_string(), &resolution[i].to_string()); // } // new_fullfilename // }