mirror of
https://git.um-react.app/um/lib_um_crypto_rust.git
synced 2026-03-08 04:29:54 +00:00
[mg3d] feat #3: implement migu 3d decipher with improved key guessing
This commit is contained in:
70
um_cli/src/cmd/mg3d.rs
Normal file
70
um_cli/src/cmd/mg3d.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use crate::Cli;
|
||||
use anyhow::{bail, Result};
|
||||
use clap::Args;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write};
|
||||
use std::path::PathBuf;
|
||||
use umc_mg3d::{guess_key, Decipher};
|
||||
|
||||
/// Decrypt a mg3d file (Migu 3D Audio)
|
||||
#[derive(Args)]
|
||||
pub struct ArgsMigu3D {
|
||||
/// Path to output file, e.g. /export/Music/song.wav
|
||||
#[clap(short, long)]
|
||||
output: PathBuf,
|
||||
|
||||
/// Path to input file, e.g. /export/Music/song.mg3d
|
||||
#[arg(name = "input")]
|
||||
input: PathBuf,
|
||||
|
||||
/// File key (androidFileKey/iosFileKey). Leave empty to guess the key.
|
||||
#[clap(short = 'k', long = "file-key")]
|
||||
file_key: Option<String>,
|
||||
}
|
||||
|
||||
impl ArgsMigu3D {
|
||||
pub fn run(&self, cli: &Cli) -> Result<i32> {
|
||||
let mut reader = BufReader::with_capacity(cli.buffer_size, File::open(&self.input)?);
|
||||
let mut writer = BufWriter::with_capacity(cli.buffer_size, File::create(&self.output)?);
|
||||
|
||||
let decipher = self.make_decipher(&mut reader)?;
|
||||
if cli.verbose {
|
||||
println!("final key: {}", hex::encode(decipher.get_key()));
|
||||
}
|
||||
|
||||
let mut offset = 0usize;
|
||||
let mut buffer = vec![0u8; cli.buffer_size];
|
||||
loop {
|
||||
let n = reader.read(&mut buffer[..])?;
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
decipher.decrypt(&mut buffer[..n], offset);
|
||||
writer.write_all(&buffer[..n])?;
|
||||
offset += n;
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn make_decipher<T>(&self, reader: &mut T) -> Result<Decipher>
|
||||
where
|
||||
T: Read + Seek + ?Sized,
|
||||
{
|
||||
let decipher = match &self.file_key {
|
||||
None => {
|
||||
let mut buffer = [0u8; 0x100];
|
||||
reader.read_exact(&mut buffer)?;
|
||||
reader.seek(SeekFrom::Current(-0x100))?;
|
||||
let key = match guess_key(&buffer) {
|
||||
Some(key) => key,
|
||||
None => bail!("failed to guess a valid key"),
|
||||
};
|
||||
Decipher::new_from_final_key(&key)?
|
||||
}
|
||||
Some(key) => Decipher::new_from_file_key(key)?,
|
||||
};
|
||||
|
||||
Ok(decipher)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ use clap::Subcommand;
|
||||
|
||||
pub mod joox;
|
||||
pub mod kgm;
|
||||
pub mod mg3d;
|
||||
pub mod ncm;
|
||||
pub mod qmc1;
|
||||
pub mod qmc2;
|
||||
@@ -11,20 +12,29 @@ pub mod xmly;
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Commands {
|
||||
#[command(name = "ncm")]
|
||||
NCM(ncm::ArgsNCM),
|
||||
|
||||
#[command(name = "kgm")]
|
||||
KGM(kgm::ArgsKGM),
|
||||
|
||||
#[command(name = "mg3d")]
|
||||
Migu3D(mg3d::ArgsMigu3D),
|
||||
|
||||
#[command(name = "joox")]
|
||||
JOOX(joox::ArgsJoox),
|
||||
|
||||
#[command(name = "qmc1")]
|
||||
QMCv1(qmc1::ArgsQMCv1),
|
||||
#[command(name = "qmc2")]
|
||||
QMCv2(qmc2::ArgsQMCv2),
|
||||
#[command(name = "ncm")]
|
||||
NCM(ncm::ArgsNCM),
|
||||
#[command(name = "kgm")]
|
||||
KGM(kgm::ArgsKGM),
|
||||
#[command(name = "joox")]
|
||||
JOOX(joox::ArgsJoox),
|
||||
#[command(name = "xmly")]
|
||||
XMLY(xmly::ArgsXimalaya),
|
||||
#[command(name = "xiami")]
|
||||
Xiami(xiami::ArgsXiami),
|
||||
|
||||
#[command(name = "qtfm")]
|
||||
QTFM(qtfm::ArgsQingTingFM),
|
||||
|
||||
#[command(name = "xiami")]
|
||||
Xiami(xiami::ArgsXiami),
|
||||
|
||||
#[command(name = "xmly")]
|
||||
XMLY(xmly::ArgsXimalaya),
|
||||
}
|
||||
|
||||
@@ -27,14 +27,15 @@ pub struct Cli {
|
||||
|
||||
fn run_command(cli: &Cli) -> Result<i32> {
|
||||
match &cli.command {
|
||||
Some(Commands::JOOX(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::KGM(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::Migu3D(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::NCM(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::QMCv1(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::QMCv2(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::NCM(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::KGM(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::JOOX(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::XMLY(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::Xiami(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::QTFM(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::Xiami(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::XMLY(cmd)) => cmd.run(&cli),
|
||||
None => {
|
||||
// https://github.com/clap-rs/clap/issues/3857#issuecomment-1161796261
|
||||
todo!("implement a sensible default command, similar to um/cli");
|
||||
|
||||
Reference in New Issue
Block a user