diff --git a/Cargo.toml b/Cargo.toml index 17e3a20..c8b2077 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,9 +24,7 @@ keywords = ["youtube", "video", "music"] categories = ["api-bindings", "multimedia"] [workspace.dependencies] -quick-js-dtp = { version = "0.4.1", default-features = false, features = [ - "patch-dateparser", -] } +rquickjs = "0.8.1" once_cell = "1.12.0" regex = "1.6.0" fancy-regex = "0.14.0" @@ -94,7 +92,7 @@ rustls-tls-webpki-roots = ["reqwest/rustls-tls-webpki-roots"] rustls-tls-native-roots = ["reqwest/rustls-tls-native-roots"] [dependencies] -quick-js-dtp.workspace = true +rquickjs.workspace = true once_cell.workspace = true regex.workspace = true fancy-regex.workspace = true diff --git a/src/deobfuscate.rs b/src/deobfuscate.rs index dc1c2c8..aab5d53 100644 --- a/src/deobfuscate.rs +++ b/src/deobfuscate.rs @@ -4,6 +4,7 @@ use once_cell::sync::Lazy; use regex::Regex; use reqwest::Client; use ress::tokens::Token; +use rquickjs::{Context, Runtime}; use serde::{Deserialize, Serialize}; use crate::{ @@ -13,7 +14,7 @@ use crate::{ }; pub struct Deobfuscator { - ctx: quick_js::Context, + ctx: Context, } #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -76,29 +77,34 @@ impl DeobfData { impl Deobfuscator { /// Instantiate a new deobfuscator with the given data pub fn new(data: &DeobfData) -> Result { - let ctx = - quick_js::Context::new().or(Err(DeobfError::Other("could not create QuickJS rt")))?; - ctx.eval(&data.sig_fn)?; - ctx.eval(&data.nsig_fn)?; - + let rt = Runtime::new()?; + let ctx = Context::full(&rt)?; + ctx.with(|ctx| { + let mut opts = rquickjs::context::EvalOptions::default(); + opts.strict = false; + ctx.eval_with_options::<(), _>(data.sig_fn.as_bytes(), opts)?; + let mut opts = rquickjs::context::EvalOptions::default(); + opts.strict = false; + ctx.eval_with_options::<(), _>(data.nsig_fn.as_bytes(), opts) + })?; Ok(Self { ctx }) } /// Deobfuscate the `s` parameter from the `signature_cipher` field pub fn deobfuscate_sig(&self, sig: &str) -> Result { - let res = self.ctx.call_function(DEOBF_SIG_FUNC_NAME, [sig])?; - - res.into_string() - .ok_or(DeobfError::Other("sig deobfuscation fn returned no string")) + let res = self + .ctx + .with(|ctx| call_fn(&ctx, DEOBF_SIG_FUNC_NAME, sig))?; + tracing::trace!("deobf sig: {sig} -> {res}"); + Ok(res) } /// Deobfuscate the `n` stream URL parameter to circumvent throttling pub fn deobfuscate_nsig(&self, nsig: &str) -> Result { - let res = self.ctx.call_function(DEOBF_NSIG_FUNC_NAME, [nsig])?; - let res = res.into_string().ok_or(DeobfError::Other( - "nsig deobfuscation fn returned no string", - ))?; - tracing::debug!("deobf nsig: {nsig} -> {res}"); + let res = self + .ctx + .with(|ctx| call_fn(&ctx, DEOBF_NSIG_FUNC_NAME, nsig))?; + tracing::trace!("deobf nsig: {nsig} -> {res}"); if res.starts_with("enhanced_except_") || res.ends_with(nsig) { return Err(DeobfError::Other("nsig fn returned an exception")); } @@ -279,6 +285,7 @@ fn extract_js_fn(js: &str, offset: usize, name: &str) -> Result Result(var_code.as_bytes())) { tracing::warn!("invalid var ({e}): {var_code}"); code = format!("var {ident}={{}}; {code}"); } else { - code = format!("var {var_code}; {code}"); + code = format!("{var_code} {code}"); } } } @@ -362,15 +369,21 @@ fn extract_js_var(js: &str) -> Option<&str> { } } +fn call_fn(ctx: &rquickjs::Ctx, fn_name: &str, arg: &str) -> Result { + let f: rquickjs::Function = ctx.globals().get(fn_name)?; + f.call((arg,)) +} + /// Verify if the deobfuscation function successfully processes a random input string fn verify_fn(js_fn: &str, fn_name: &str) -> Result<(), DeobfError> { - let ctx = quick_js::Context::new().or(Err(DeobfError::Other("could not create QuickJS rt")))?; - ctx.eval(js_fn)?; + let rt = Runtime::new()?; + let ctx = Context::full(&rt)?; let testinp = util::generate_content_playback_nonce(); - let res = ctx - .call_function(fn_name, [testinp.to_owned()])? - .into_string() - .ok_or(DeobfError::Other("deobfuscation fn returned no string"))?; + let res = ctx.with(|ctx| { + ctx.eval::<(), _>(js_fn)?; + call_fn(&ctx, fn_name, &testinp) + })?; + if res.is_empty() { return Err(DeobfError::Other("deobfuscation fn returned empty string")); } diff --git a/src/error.rs b/src/error.rs index a2125e8..f41cd8a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -156,7 +156,7 @@ pub(crate) mod internal { pub enum DeobfError { /// Error during JavaScript execution #[error("js execution error: {0}")] - JavaScript(#[from] quick_js::ExecutionError), + JavaScript(#[from] rquickjs::Error), /// Error during JavaScript parsing #[error("js parsing: {0}")] JsParser(#[from] ress::error::Error),