refactored, started with get_player_js (TEMP)
This commit is contained in:
parent
0c76125595
commit
0acf8a9fb9
5 changed files with 307 additions and 333 deletions
|
|
@ -14,10 +14,5 @@ anyhow = "1.0"
|
|||
thiserror = "1.0.31"
|
||||
url = "2.2.2"
|
||||
log = "0.4.17"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3.6"
|
||||
|
||||
[[bench]]
|
||||
name = "my_benchmark"
|
||||
harness = false
|
||||
reqwest = "0.11.11"
|
||||
tokio = {version = "1.20.0", features = ["macros"]}
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use rusty_pipe::*;
|
||||
|
||||
const TEST_JS: &str = include_str!("../notes/base.js");
|
||||
|
||||
fn bench_match_to_closing_parenthesis(c: &mut Criterion) {
|
||||
c.bench_function("match_to_closing_parenthesis", |b| {
|
||||
b.iter(|| match_to_closing_parenthesis(TEST_JS, "Vo=function"))
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_sig(c: &mut Criterion) {
|
||||
c.bench_function("deobf_sig", |b| {
|
||||
b.iter(|| {
|
||||
let dcode = load_deobfuscation_code(TEST_JS).unwrap();
|
||||
deobfuscate_signature("GOqGOqGOq0QJ8wRAIgaryQHfplJ9xJSKFywyaSMHuuwZYsoMTAvRvfm51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5fb5i", &dcode).unwrap()
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("deobf_nsig", |b| {
|
||||
b.iter(|| {
|
||||
let name = get_n_deobfuscation_function_name(TEST_JS).unwrap();
|
||||
let dcode = parse_n_decode_function(TEST_JS, &name).unwrap();
|
||||
deobfuscate_n_signature(&dcode, &name, "BI_n4PxQ22is-KKajKUW").unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_sig_cached(c: &mut Criterion) {
|
||||
let sig_dcode = load_deobfuscation_code(TEST_JS).unwrap();
|
||||
let nsig_name = get_n_deobfuscation_function_name(TEST_JS).unwrap();
|
||||
let nsig_dcode = parse_n_decode_function(TEST_JS, &nsig_name).unwrap();
|
||||
|
||||
c.bench_function("deobf_sig_cached", |b| {
|
||||
b.iter(|| deobfuscate_signature("GOqGOqGOq0QJ8wRAIgaryQHfplJ9xJSKFywyaSMHuuwZYsoMTAvRvfm51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5fb5i", &sig_dcode).unwrap())
|
||||
});
|
||||
|
||||
c.bench_function("deobf_sig_cached", |b| {
|
||||
b.iter(|| deobfuscate_n_signature(&nsig_dcode, &nsig_name, "BI_n4PxQ22is-KKajKUW").unwrap())
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_match_to_closing_parenthesis, bench_sig, bench_sig_cached);
|
||||
criterion_main!(benches);
|
||||
303
src/deobfuscate.rs
Normal file
303
src/deobfuscate.rs
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
use anyhow::{anyhow, bail, Result};
|
||||
use fancy_regex::Regex;
|
||||
use once_cell::sync::Lazy;
|
||||
use quick_js::Context;
|
||||
use std::result::Result::Ok;
|
||||
|
||||
pub struct Deobfuscator {
|
||||
// js_url: String,
|
||||
// last_update
|
||||
sig_fn: String,
|
||||
nsig_fn: String,
|
||||
}
|
||||
|
||||
impl Deobfuscator {
|
||||
pub fn from_js(player_js: &str) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sig_fn: get_sig_fn(player_js)?,
|
||||
nsig_fn: get_nsig_fn(player_js)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn deobfuscate_sig(&self, sig: &str) -> Result<String> {
|
||||
deobfuscate_sig(sig, &self.sig_fn)
|
||||
}
|
||||
|
||||
pub fn deobfuscate_nsig(&self, nsig: &str) -> Result<String> {
|
||||
deobfuscate_nsig(nsig, &self.nsig_fn)
|
||||
}
|
||||
}
|
||||
|
||||
const DEOBFUSCATION_FUNC_NAME: &str = "deobfuscate";
|
||||
|
||||
fn get_sig_fn_name(player_js: &str) -> Result<String> {
|
||||
static FUNCTION_PATTERNS: Lazy<[Regex; 6]> = Lazy::new(|| {
|
||||
[
|
||||
Regex::new("(?:\\b|[^a-zA-Z0-9$])([a-zA-Z0-9$]{2,})\\s*=\\s*function\\(\\s*a\\s*\\)\\s*\\{\\s*a\\s*=\\s*a\\.split\\(\\s*\"\"\\s*\\)").unwrap(),
|
||||
Regex::new("\\bm=([a-zA-Z0-9$]{2,})\\(decodeURIComponent\\(h\\.s\\)\\)").unwrap(),
|
||||
Regex::new("\\bc&&\\(c=([a-zA-Z0-9$]{2,})\\(decodeURIComponent\\(c\\)\\)").unwrap(),
|
||||
Regex::new("([\\w$]+)\\s*=\\s*function\\((\\w+)\\)\\{\\s*\\2=\\s*\\2\\.split\\(\"\"\\)\\s*;").unwrap(),
|
||||
Regex::new("\\b([\\w$]{2,})\\s*=\\s*function\\((\\w+)\\)\\{\\s*\\2=\\s*\\2\\.split\\(\"\"\\)\\s*;").unwrap(),
|
||||
Regex::new("\\bc\\s*&&\\s*d\\.set\\([^,]+\\s*,\\s*(:encodeURIComponent\\s*\\()([a-zA-Z0-9$]+)\\(").unwrap(),
|
||||
]
|
||||
});
|
||||
|
||||
FUNCTION_PATTERNS
|
||||
.iter()
|
||||
.find_map(|pattern| pattern.captures(player_js).ok().flatten())
|
||||
.map(|c| c.get(1).unwrap().as_str().to_string())
|
||||
.ok_or_else(|| anyhow!("could not find deobf function name"))
|
||||
}
|
||||
|
||||
fn caller_function(fn_name: &str) -> String {
|
||||
"function ".to_string() + DEOBFUSCATION_FUNC_NAME + "(a){return " + &fn_name + "(a);}"
|
||||
}
|
||||
|
||||
fn get_sig_fn(player_js: &str) -> Result<String> {
|
||||
let dfunc_name = get_sig_fn_name(player_js)?;
|
||||
|
||||
let function_pattern_str = "(".to_string()
|
||||
+ &dfunc_name.replace('$', "\\$")
|
||||
+ "=function\\([a-zA-Z0-9_]+\\)\\{.+?\\})";
|
||||
let function_pattern = ok_or_bail!(
|
||||
Regex::new(&function_pattern_str),
|
||||
Err(anyhow!("could not parse function pattern regex"))
|
||||
);
|
||||
|
||||
let deobfuscate_function = "var ".to_string()
|
||||
+ some_or_bail!(
|
||||
function_pattern.captures(player_js).ok().flatten(),
|
||||
Err(anyhow!("could not find deobf function"))
|
||||
)
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
+ ";";
|
||||
|
||||
let helper_object_name_pattern = Regex::new(";([A-Za-z0-9_\\$]{2})\\...\\(").unwrap();
|
||||
let helper_object_name = some_or_bail!(
|
||||
helper_object_name_pattern
|
||||
.captures(&deobfuscate_function)
|
||||
.ok()
|
||||
.flatten(),
|
||||
Err(anyhow!("could not find helper object name"))
|
||||
)
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str();
|
||||
|
||||
let helper_pattern_str =
|
||||
"(var ".to_string() + &helper_object_name.replace('$', "\\$") + "=\\{.+?\\}\\};)";
|
||||
let helper_pattern = ok_or_bail!(
|
||||
Regex::new(&helper_pattern_str),
|
||||
Err(anyhow!("could not parse helper pattern regex"))
|
||||
);
|
||||
let player_js_nonl = player_js.replace('\n', "");
|
||||
let helper_object = some_or_bail!(
|
||||
helper_pattern.captures(&player_js_nonl).ok().flatten(),
|
||||
Err(anyhow!("could not find helper object"))
|
||||
)
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str();
|
||||
|
||||
Ok(helper_object.to_string() + &deobfuscate_function + &caller_function(&dfunc_name))
|
||||
}
|
||||
|
||||
fn deobfuscate_sig(sig: &str, sig_fn: &str) -> Result<String> {
|
||||
let context = Context::new()?;
|
||||
context.eval(sig_fn)?;
|
||||
let res = context.call_function(DEOBFUSCATION_FUNC_NAME, vec![sig])?;
|
||||
|
||||
match res.as_str() {
|
||||
Some(res) => Ok(res.to_string()),
|
||||
None => bail!("deobfuscation func returned null"),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_nsig_fn_name(player_js: &str) -> Result<String> {
|
||||
let function_name_pattern =
|
||||
Regex::new("\\.get\\(\"n\"\\)\\)&&\\(b=([a-zA-Z0-9$]+)(?:\\[(\\d+)])?\\([a-zA-Z0-9]\\)")
|
||||
.unwrap();
|
||||
|
||||
let fname_match = some_or_bail!(
|
||||
function_name_pattern.captures(player_js).ok().flatten(),
|
||||
Err(anyhow!("could not find n_deobf function"))
|
||||
);
|
||||
|
||||
let function_name = fname_match.get(1).unwrap().as_str();
|
||||
|
||||
if fname_match.len() == 1 {
|
||||
return Ok(function_name.to_string());
|
||||
}
|
||||
|
||||
let array_num = fname_match.get(2).unwrap().as_str().parse::<u32>()?;
|
||||
let array_pattern_str =
|
||||
"var ".to_string() + &fancy_regex::escape(function_name) + "\\s*=\\s*\\[(.+?)];";
|
||||
let array_pattern = Regex::new(&array_pattern_str)?;
|
||||
let array_str = some_or_bail!(
|
||||
array_pattern.captures(player_js).ok().flatten(),
|
||||
Err(anyhow!("could not find n_deobf array_str"))
|
||||
)
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str();
|
||||
let mut names = array_str.split(',');
|
||||
let name = some_or_bail!(
|
||||
names.nth(array_num.try_into()?),
|
||||
Err(anyhow!(
|
||||
"could not get {}th item from {}",
|
||||
array_num,
|
||||
array_str
|
||||
))
|
||||
);
|
||||
Ok(name.to_string())
|
||||
}
|
||||
|
||||
fn match_to_closing_parenthesis(string: &str, start: &str) -> Option<String> {
|
||||
let mut start_index = string.find(start)?;
|
||||
start_index += start.len();
|
||||
|
||||
let mut visited_par = false;
|
||||
let mut open_par = 0;
|
||||
let mut res = String::new();
|
||||
|
||||
for c in string[start_index..].chars() {
|
||||
res.push(c);
|
||||
|
||||
match c {
|
||||
'{' => {
|
||||
visited_par = true;
|
||||
open_par += 1;
|
||||
}
|
||||
'}' => {
|
||||
open_par -= 1;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if visited_par && open_par == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn get_nsig_fn(player_js: &str) -> Result<String> {
|
||||
let function_name = get_nsig_fn_name(player_js)?;
|
||||
|
||||
// Find using parentheses
|
||||
let function_base = function_name.to_string() + "=function";
|
||||
let nsig_fn_code = match match_to_closing_parenthesis(player_js, &function_base) {
|
||||
Some(m) => function_base.clone() + &m + ";",
|
||||
None => {
|
||||
// Find using regex
|
||||
let player_js_nonl = player_js.replace('\n', "");
|
||||
|
||||
let function_pattern_str = function_name.to_string() + "=function(.*?}};)\n";
|
||||
let function_pattern = Regex::new(&function_pattern_str)?;
|
||||
let function = some_or_bail!(
|
||||
function_pattern.captures(&player_js_nonl)?,
|
||||
Err(anyhow!("could not find n_decode function"))
|
||||
)
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str();
|
||||
|
||||
"function ".to_string() + function
|
||||
}
|
||||
};
|
||||
|
||||
Ok(nsig_fn_code + &caller_function(&function_name))
|
||||
}
|
||||
|
||||
fn deobfuscate_nsig(sig: &str, nsig_fn: &str) -> Result<String> {
|
||||
let context = quick_js::Context::new()?;
|
||||
context.eval(nsig_fn)?;
|
||||
let res = context.call_function(DEOBFUSCATION_FUNC_NAME, vec![sig])?;
|
||||
|
||||
match res.as_str() {
|
||||
Some(res) => Ok(res.to_string()),
|
||||
None => bail!("deobfuscation func returned null"),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_player_js_url() -> Result<String> {
|
||||
let resp = reqwest::get("https://www.youtube.com/iframe_api").await?;
|
||||
let text = resp.text().await?;
|
||||
println!("{}", text);
|
||||
|
||||
Ok("x".to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const TEST_JS: &str = include_str!("../notes/base.js");
|
||||
const N_DEOBF_FUNC: &str = r#"Vo=function(a){var b=a.split(""),c=[function(d,e,f){var h=f.length;d.forEach(function(l,m,n){this.push(n[m]=f[(f.indexOf(l)-f.indexOf(this[m])+m+h--)%f.length])},e.split(""))},
|
||||
928409064,-595856984,1403221911,653089124,-168714481,-1883008765,158931990,1346921902,361518508,1403221911,-362174697,-233641452,function(){for(var d=64,e=[];++d-e.length-32;){switch(d){case 91:d=44;continue;case 123:d=65;break;case 65:d-=18;continue;case 58:d=96;continue;case 46:d=95}e.push(String.fromCharCode(d))}return e},
|
||||
b,158931990,791141857,-907319795,-1776185924,1595027902,-829736173,function(d,e){e=(e%d.length+d.length)%d.length;d.splice(0,1,d.splice(e,1,d[0])[0])},
|
||||
-1274951142,function(){for(var d=64,e=[];++d-e.length-32;){switch(d){case 91:d=44;continue;case 123:d=65;break;case 65:d-=18;continue;case 58:d=96;continue;case 46:d=95}e.push(String.fromCharCode(d))}return e},
|
||||
1758743891,function(d){d.reverse()},
|
||||
-830417133,"AF43j",1942017693,function(d,e){e=(e%d.length+d.length)%d.length;d.splice(e,1)},
|
||||
null,-959991459,-287691724,-1365731946,b,1250397544,-1883008765,-1912322658,b,1300441121,null,-1962382380,1954679120,function(d){for(var e=d.length;e;)d.push(d.splice(--e,1)[0])},
|
||||
-985125467,function(d,e){for(e=(e%d.length+d.length)%d.length;e--;)d.unshift(d.pop())},
|
||||
null,497372841,-1912651541,function(d,e){d.push(e)},
|
||||
function(d,e){e=(e%d.length+d.length)%d.length;d.splice(-e).reverse().forEach(function(f){d.unshift(f)})},
|
||||
function(d,e){e=(e%d.length+d.length)%d.length;var f=d[0];d[0]=d[e];d[e]=f}];
|
||||
c[30]=c;c[40]=c;c[46]=c;try{c[43](c[34]),c[45](c[40],c[47]),c[46](c[51],c[33]),c[16](c[47],c[36]),c[38](c[31],c[49]),c[16](c[11],c[39]),c[0](c[11]),c[35](c[0],c[30]),c[35](c[4],c[17]),c[34](c[48],c[7],c[11]()),c[35](c[4],c[23]),c[35](c[4],c[9]),c[5](c[48],c[28]),c[36](c[46],c[16]),c[4](c[41],c[1]),c[4](c[16],c[28]),c[3](c[40],c[17]),c[9](c[8],c[23]),c[45](c[30],c[4]),c[50](c[3],c[28]),c[36](c[51],c[23]),c[14](c[0],c[24]),c[14](c[35],c[1]),c[20](c[51],c[41]),c[15](c[8],c[0]),c[31](c[35]),c[29](c[26]),
|
||||
c[36](c[8],c[32]),c[20](c[25],c[10]),c[2](c[22],c[8]),c[32](c[20],c[16]),c[32](c[47],c[49]),c[1](c[44],c[28]),c[39](c[16]),c[32](c[42],c[22]),c[46](c[14],c[48]),c[26](c[29],c[10]),c[46](c[9],c[3]),c[32](c[45])}catch(d){return"enhanced_except_85UBjOr-_w8_"+a}return b.join("")};function deobfuscate(a){return Vo(a);}"#;
|
||||
|
||||
#[test]
|
||||
fn test_get_sig_fn_name() {
|
||||
let dfunc_name = get_sig_fn_name(TEST_JS).unwrap();
|
||||
assert_eq!(dfunc_name, "Rva");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_sig_fn() {
|
||||
let dcode = get_sig_fn(TEST_JS).unwrap();
|
||||
assert_eq!(
|
||||
dcode,
|
||||
r#"var qB={w8:function(a){a.reverse()},EC:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c},Np:function(a,b){a.splice(0,b)}};var Rva=function(a){a=a.split("");qB.Np(a,3);qB.w8(a,41);qB.EC(a,55);qB.Np(a,3);qB.w8(a,33);qB.Np(a,3);qB.EC(a,48);qB.EC(a,17);qB.EC(a,43);return a.join("")};function deobfuscate(a){return Rva(a);}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deobfuscate_sig() {
|
||||
let dcode = get_sig_fn(TEST_JS).unwrap();
|
||||
let deobf = deobfuscate_sig("GOqGOqGOq0QJ8wRAIgaryQHfplJ9xJSKFywyaSMHuuwZYsoMTAvRvfm51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5fb5i", &dcode).unwrap();
|
||||
assert_eq!(deobf, "AOq0QJ8wRAIgaryQHmplJ9xJSKFywyaSMHuuwZYsoMTfvRviG51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5f");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_nsig_fn_name() {
|
||||
let name = get_nsig_fn_name(TEST_JS).unwrap();
|
||||
assert_eq!(name, "Vo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match_to_closing_parenthesis() {
|
||||
let res =
|
||||
match_to_closing_parenthesis("Kx Hello { Thx { Bye } } Wut {Tst {}}", "Hello").unwrap();
|
||||
assert_eq!(res, " { Thx { Bye } }")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_nsig_fn() {
|
||||
let res = get_nsig_fn(TEST_JS).unwrap();
|
||||
assert_eq!(res, N_DEOBF_FUNC);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deobfuscate_nsig() {
|
||||
let res = deobfuscate_nsig("BI_n4PxQ22is-KKajKUW", N_DEOBF_FUNC).unwrap();
|
||||
assert_eq!(res, "nrkec0fwgTWolw");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_player_js_url() {
|
||||
let x = get_player_js_url().await.unwrap();
|
||||
}
|
||||
}
|
||||
269
src/lib.rs
269
src/lib.rs
|
|
@ -1,268 +1,3 @@
|
|||
use anyhow::{anyhow, bail, Result};
|
||||
use fancy_regex::Regex;
|
||||
use once_cell::sync::Lazy;
|
||||
use quick_js::Context;
|
||||
use std::result::Result::Ok;
|
||||
#[macro_use] mod macros;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
const DEOBFUSCATION_FUNC_NAME: &str = "deobfuscate";
|
||||
|
||||
fn get_deobfuscation_func_name(player_js: &str) -> Result<String> {
|
||||
static FUNCTION_PATTERNS: Lazy<[Regex; 6]> = Lazy::new(|| {
|
||||
[
|
||||
Regex::new("(?:\\b|[^a-zA-Z0-9$])([a-zA-Z0-9$]{2,})\\s*=\\s*function\\(\\s*a\\s*\\)\\s*\\{\\s*a\\s*=\\s*a\\.split\\(\\s*\"\"\\s*\\)").unwrap(),
|
||||
Regex::new("\\bm=([a-zA-Z0-9$]{2,})\\(decodeURIComponent\\(h\\.s\\)\\)").unwrap(),
|
||||
Regex::new("\\bc&&\\(c=([a-zA-Z0-9$]{2,})\\(decodeURIComponent\\(c\\)\\)").unwrap(),
|
||||
Regex::new("([\\w$]+)\\s*=\\s*function\\((\\w+)\\)\\{\\s*\\2=\\s*\\2\\.split\\(\"\"\\)\\s*;").unwrap(),
|
||||
Regex::new("\\b([\\w$]{2,})\\s*=\\s*function\\((\\w+)\\)\\{\\s*\\2=\\s*\\2\\.split\\(\"\"\\)\\s*;").unwrap(),
|
||||
Regex::new("\\bc\\s*&&\\s*d\\.set\\([^,]+\\s*,\\s*(:encodeURIComponent\\s*\\()([a-zA-Z0-9$]+)\\(").unwrap(),
|
||||
]
|
||||
});
|
||||
|
||||
FUNCTION_PATTERNS
|
||||
.iter()
|
||||
.find_map(|pattern| pattern.captures(player_js).ok().flatten())
|
||||
.map(|c| c.get(1).unwrap().as_str().to_string())
|
||||
.ok_or_else(|| anyhow!("could not find deobf function name"))
|
||||
}
|
||||
|
||||
pub fn load_deobfuscation_code(player_js: &str) -> Result<String> {
|
||||
let dfunc_name = get_deobfuscation_func_name(player_js)?;
|
||||
|
||||
let function_pattern_str = "(".to_string()
|
||||
+ &dfunc_name.replace('$', "\\$")
|
||||
+ "=function\\([a-zA-Z0-9_]+\\)\\{.+?\\})";
|
||||
let function_pattern = ok_or_bail!(
|
||||
Regex::new(&function_pattern_str),
|
||||
Err(anyhow!("could not parse function pattern regex"))
|
||||
);
|
||||
|
||||
let deobfuscate_function = "var ".to_string()
|
||||
+ some_or_bail!(
|
||||
function_pattern.captures(player_js).ok().flatten(),
|
||||
Err(anyhow!("could not find deobf function"))
|
||||
)
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
+ ";";
|
||||
|
||||
let helper_object_name_pattern = Regex::new(";([A-Za-z0-9_\\$]{2})\\...\\(").unwrap();
|
||||
let helper_object_name = some_or_bail!(
|
||||
helper_object_name_pattern
|
||||
.captures(&deobfuscate_function)
|
||||
.ok()
|
||||
.flatten(),
|
||||
Err(anyhow!("could not find helper object name"))
|
||||
)
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str();
|
||||
|
||||
let helper_pattern_str =
|
||||
"(var ".to_string() + &helper_object_name.replace('$', "\\$") + "=\\{.+?\\}\\};)";
|
||||
let helper_pattern = ok_or_bail!(
|
||||
Regex::new(&helper_pattern_str),
|
||||
Err(anyhow!("could not parse helper pattern regex"))
|
||||
);
|
||||
let player_js_nonl = player_js.replace('\n', "");
|
||||
let helper_object = some_or_bail!(
|
||||
helper_pattern.captures(&player_js_nonl).ok().flatten(),
|
||||
Err(anyhow!("could not find helper object"))
|
||||
)
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str();
|
||||
|
||||
let caller_function =
|
||||
"function ".to_string() + DEOBFUSCATION_FUNC_NAME + "(a){return " + &dfunc_name + "(a);}";
|
||||
|
||||
Ok(helper_object.to_string() + &deobfuscate_function + &caller_function)
|
||||
}
|
||||
|
||||
pub fn deobfuscate_signature(obfuscated_sig: &str, deobfuscation_code: &str) -> Result<String> {
|
||||
let context = Context::new()?;
|
||||
context.eval(deobfuscation_code)?;
|
||||
let res = context.call_function(DEOBFUSCATION_FUNC_NAME, vec![obfuscated_sig])?;
|
||||
|
||||
match res.as_str() {
|
||||
Some(res) => Ok(res.to_string()),
|
||||
None => bail!("deobfuscation func returned null"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_n_deobfuscation_function_name(player_js: &str) -> Result<String> {
|
||||
let function_name_pattern =
|
||||
Regex::new("\\.get\\(\"n\"\\)\\)&&\\(b=([a-zA-Z0-9$]+)(?:\\[(\\d+)])?\\([a-zA-Z0-9]\\)")
|
||||
.unwrap();
|
||||
|
||||
let fname_match = some_or_bail!(
|
||||
function_name_pattern.captures(player_js).ok().flatten(),
|
||||
Err(anyhow!("could not find n_deobf function"))
|
||||
);
|
||||
|
||||
let function_name = fname_match.get(1).unwrap().as_str();
|
||||
|
||||
if fname_match.len() == 1 {
|
||||
return Ok(function_name.to_string());
|
||||
}
|
||||
|
||||
let array_num = fname_match.get(2).unwrap().as_str().parse::<u32>()?;
|
||||
let array_pattern_str =
|
||||
"var ".to_string() + &fancy_regex::escape(function_name) + "\\s*=\\s*\\[(.+?)];";
|
||||
let array_pattern = Regex::new(&array_pattern_str)?;
|
||||
let array_str = some_or_bail!(
|
||||
array_pattern.captures(player_js).ok().flatten(),
|
||||
Err(anyhow!("could not find n_deobf array_str"))
|
||||
)
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str();
|
||||
let mut names = array_str.split(',');
|
||||
let name = some_or_bail!(
|
||||
names.nth(array_num.try_into()?),
|
||||
Err(anyhow!(
|
||||
"could not get {}th item from {}",
|
||||
array_num,
|
||||
array_str
|
||||
))
|
||||
);
|
||||
Ok(name.to_string())
|
||||
}
|
||||
|
||||
pub fn match_to_closing_parenthesis(string: &str, start: &str) -> Option<String> {
|
||||
let mut start_index = string.find(start)?;
|
||||
start_index += start.len();
|
||||
|
||||
let mut visited_par = false;
|
||||
let mut open_par = 0;
|
||||
let mut res = String::new();
|
||||
|
||||
for c in string[start_index..].chars() {
|
||||
res.push(c);
|
||||
|
||||
match c {
|
||||
'{' => {
|
||||
visited_par = true;
|
||||
open_par += 1;
|
||||
}
|
||||
'}' => {
|
||||
open_par -= 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if visited_par && open_par == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn parse_n_decode_function(player_js: &str, function_name: &str) -> Result<String> {
|
||||
// Find using parentheses
|
||||
let function_base = function_name.to_string() + "=function";
|
||||
match match_to_closing_parenthesis(player_js, &function_base) {
|
||||
Some(m) => Ok(function_base.clone() + &m + ";"),
|
||||
None => {
|
||||
// Find using regex
|
||||
let player_js_nonl = player_js.replace('\n', "");
|
||||
|
||||
let function_pattern_str = function_name.to_string() + "=function(.*?}};)\n";
|
||||
let function_pattern = Regex::new(&function_pattern_str)?;
|
||||
let function = some_or_bail!(
|
||||
function_pattern.captures(&player_js_nonl)?,
|
||||
Err(anyhow!("could not find n_decode function"))
|
||||
)
|
||||
.get(1)
|
||||
.unwrap()
|
||||
.as_str();
|
||||
|
||||
Ok("function ".to_string() + function)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deobfuscate_n_signature(
|
||||
function: &str,
|
||||
function_name: &str,
|
||||
obfuscated_sig: &str,
|
||||
) -> Result<String> {
|
||||
let context = quick_js::Context::new()?;
|
||||
context.eval(function)?;
|
||||
let res = context.call_function(function_name, vec![obfuscated_sig])?;
|
||||
|
||||
match res.as_str() {
|
||||
Some(res) => Ok(res.to_string()),
|
||||
None => bail!("deobfuscation func returned null"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const TEST_JS: &str = include_str!("../notes/base.js");
|
||||
const N_DEOBF_FUNC: &str = r#"Vo=function(a){var b=a.split(""),c=[function(d,e,f){var h=f.length;d.forEach(function(l,m,n){this.push(n[m]=f[(f.indexOf(l)-f.indexOf(this[m])+m+h--)%f.length])},e.split(""))},
|
||||
928409064,-595856984,1403221911,653089124,-168714481,-1883008765,158931990,1346921902,361518508,1403221911,-362174697,-233641452,function(){for(var d=64,e=[];++d-e.length-32;){switch(d){case 91:d=44;continue;case 123:d=65;break;case 65:d-=18;continue;case 58:d=96;continue;case 46:d=95}e.push(String.fromCharCode(d))}return e},
|
||||
b,158931990,791141857,-907319795,-1776185924,1595027902,-829736173,function(d,e){e=(e%d.length+d.length)%d.length;d.splice(0,1,d.splice(e,1,d[0])[0])},
|
||||
-1274951142,function(){for(var d=64,e=[];++d-e.length-32;){switch(d){case 91:d=44;continue;case 123:d=65;break;case 65:d-=18;continue;case 58:d=96;continue;case 46:d=95}e.push(String.fromCharCode(d))}return e},
|
||||
1758743891,function(d){d.reverse()},
|
||||
-830417133,"AF43j",1942017693,function(d,e){e=(e%d.length+d.length)%d.length;d.splice(e,1)},
|
||||
null,-959991459,-287691724,-1365731946,b,1250397544,-1883008765,-1912322658,b,1300441121,null,-1962382380,1954679120,function(d){for(var e=d.length;e;)d.push(d.splice(--e,1)[0])},
|
||||
-985125467,function(d,e){for(e=(e%d.length+d.length)%d.length;e--;)d.unshift(d.pop())},
|
||||
null,497372841,-1912651541,function(d,e){d.push(e)},
|
||||
function(d,e){e=(e%d.length+d.length)%d.length;d.splice(-e).reverse().forEach(function(f){d.unshift(f)})},
|
||||
function(d,e){e=(e%d.length+d.length)%d.length;var f=d[0];d[0]=d[e];d[e]=f}];
|
||||
c[30]=c;c[40]=c;c[46]=c;try{c[43](c[34]),c[45](c[40],c[47]),c[46](c[51],c[33]),c[16](c[47],c[36]),c[38](c[31],c[49]),c[16](c[11],c[39]),c[0](c[11]),c[35](c[0],c[30]),c[35](c[4],c[17]),c[34](c[48],c[7],c[11]()),c[35](c[4],c[23]),c[35](c[4],c[9]),c[5](c[48],c[28]),c[36](c[46],c[16]),c[4](c[41],c[1]),c[4](c[16],c[28]),c[3](c[40],c[17]),c[9](c[8],c[23]),c[45](c[30],c[4]),c[50](c[3],c[28]),c[36](c[51],c[23]),c[14](c[0],c[24]),c[14](c[35],c[1]),c[20](c[51],c[41]),c[15](c[8],c[0]),c[31](c[35]),c[29](c[26]),
|
||||
c[36](c[8],c[32]),c[20](c[25],c[10]),c[2](c[22],c[8]),c[32](c[20],c[16]),c[32](c[47],c[49]),c[1](c[44],c[28]),c[39](c[16]),c[32](c[42],c[22]),c[46](c[14],c[48]),c[26](c[29],c[10]),c[46](c[9],c[3]),c[32](c[45])}catch(d){return"enhanced_except_85UBjOr-_w8_"+a}return b.join("")};"#;
|
||||
|
||||
#[test]
|
||||
fn test_get_deobfuscation_func_name() {
|
||||
let dfunc_name = get_deobfuscation_func_name(TEST_JS).unwrap();
|
||||
assert_eq!(dfunc_name, "Rva");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_deobfuscation_code() {
|
||||
let dcode = load_deobfuscation_code(TEST_JS).unwrap();
|
||||
assert_eq!(
|
||||
dcode,
|
||||
r#"var qB={w8:function(a){a.reverse()},EC:function(a,b){var c=a[0];a[0]=a[b%a.length];a[b%a.length]=c},Np:function(a,b){a.splice(0,b)}};var Rva=function(a){a=a.split("");qB.Np(a,3);qB.w8(a,41);qB.EC(a,55);qB.Np(a,3);qB.w8(a,33);qB.Np(a,3);qB.EC(a,48);qB.EC(a,17);qB.EC(a,43);return a.join("")};function deobfuscate(a){return Rva(a);}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deobfuscate_signature() {
|
||||
let dcode = load_deobfuscation_code(TEST_JS).unwrap();
|
||||
let deobf = deobfuscate_signature("GOqGOqGOq0QJ8wRAIgaryQHfplJ9xJSKFywyaSMHuuwZYsoMTAvRvfm51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5fb5i", &dcode).unwrap();
|
||||
assert_eq!(deobf, "AOq0QJ8wRAIgaryQHmplJ9xJSKFywyaSMHuuwZYsoMTfvRviG51qIGECIA5061zWeyfMPX9hEl_U6f9J0tr7GTJMKyPf5XNrJb5f");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_n_deobfuscation_function_name() {
|
||||
let name = get_n_deobfuscation_function_name(TEST_JS).unwrap();
|
||||
assert_eq!(name, "Vo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match_to_closing_parenthesis() {
|
||||
let res =
|
||||
match_to_closing_parenthesis("Kx Hello { Thx { Bye } } Wut {Tst {}}", "Hello").unwrap();
|
||||
assert_eq!(res, " { Thx { Bye } }")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_n_decode_function() {
|
||||
let res = parse_n_decode_function(TEST_JS, "Vo").unwrap();
|
||||
assert_eq!(res, N_DEOBF_FUNC);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deobfuscate_n_signature() {
|
||||
let res = deobfuscate_n_signature(N_DEOBF_FUNC, "Vo", "BI_n4PxQ22is-KKajKUW").unwrap();
|
||||
assert_eq!(res, "nrkec0fwgTWolw");
|
||||
}
|
||||
}
|
||||
mod deobfuscate;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,3 @@
|
|||
/// Aggressively prints to the terminal. Useful for rapid debugging in a sea of
|
||||
/// terminal output.
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! highlight {
|
||||
($($arg:tt)+) => (
|
||||
{
|
||||
let indent = ">>>>>";
|
||||
let focus = ">>>>>>";
|
||||
let start = ">>>";
|
||||
let end = ">>>";
|
||||
println!("{}\n{}\n{} {}\n{}\n{}", start,indent,focus,format!($($arg)+),indent,end);
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an unwrapped Option if Some() otherwise returns the passed expression
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! some_or_bail {
|
||||
|
|
|
|||
Reference in a new issue