fix: deobfuscator: handle global functions as well

This commit is contained in:
ThetaDev 2025-03-26 02:12:18 +01:00
parent b91fd97851
commit 06550c7a5a

View file

@ -195,20 +195,48 @@ fn extract_js_fn(js: &str, name: &str) -> Result<String, DeobfError> {
let mut end = 0usize; let mut end = 0usize;
let mut period_before = false; let mut period_before = false;
let mut last_ident = None; let mut function_before = false;
let mut idents: HashMap<String, bool> = HashMap::new(); let mut idents: HashMap<String, bool> = HashMap::new();
// Set if the current statement is a variable/function param definition // Set if the current statement is a variable/function param definition
// First value is the brace level, second is true if we are on the right hand side of an assignment // First value is the brace level, second is true if we are on the right hand side of an assignment
let mut var_def_stmt: Option<(Level, bool)> = None; let mut var_def_stmt: Option<(Level, bool)> = None;
let global_objects = [ let global_objects = [
"NaN", "Infinity", "Object", "Function", "Boolean", "Symbol", "Error", "Number", "BigInt", "globalThis",
"Math", "Date", "String", "RegExp", "Array", "Map", "Set", "NaN",
"undefined",
"Infinity",
"Object",
"Function",
"Boolean",
"Symbol",
"Error",
"Number",
"BigInt",
"Math",
"Date",
"String",
"RegExp",
"Array",
"Map",
"Set",
"eval",
"isFinite",
"isNaN",
"parseFloat",
"parseInt",
"decodeURI",
"decodeURIComponent",
"encodeURI",
"encodeURIComponent",
"escape",
"unescape",
]; ];
for item in scan { for item in scan {
let it = item?; let it = item?;
let token = it.token; let token = it.token;
match state { match state {
// Looking for fn name // Looking for fn name
0 => { 0 => {
@ -226,7 +254,8 @@ fn extract_js_fn(js: &str, name: &str) -> Result<String, DeobfError> {
} }
} }
2 => { 2 => {
if let Token::Punct(punct) = token { match &token {
Token::Punct(punct) => {
let var_def_this_lvl = || { let var_def_this_lvl = || {
var_def_stmt var_def_stmt
.as_ref() .as_ref()
@ -289,8 +318,8 @@ fn extract_js_fn(js: &str, name: &str) -> Result<String, DeobfError> {
} }
_ => {} _ => {}
} }
} else if let Token::Keyword(kw) = &token { }
match kw { Token::Keyword(kw) => match kw {
Keyword::Var(_) | Keyword::Let(_) | Keyword::Const(_) => { Keyword::Var(_) | Keyword::Let(_) | Keyword::Const(_) => {
var_def_stmt = Some((level.clone(), false)); var_def_stmt = Some((level.clone(), false));
} }
@ -300,33 +329,34 @@ fn extract_js_fn(js: &str, name: &str) -> Result<String, DeobfError> {
var_def_stmt = Some((l, false)); var_def_stmt = Some((l, false));
} }
_ => {} _ => {}
} },
} Token::Ident(id) => {
// Looking for variable names
if let Token::Ident(id) = &token {
// Ignore object attributes and 1char long local vars // Ignore object attributes and 1char long local vars
if !period_before && id.as_ref().len() > 1 { if !period_before
&& id.as_ref().len() > 1
&& !global_objects.contains(&id.as_ref())
{
// If we are on the left hand side of a variable definition statement
// or after "function", mark the variable name as defined
if var_def_stmt if var_def_stmt
.as_ref() .as_ref()
.map(|(lvl, rhs)| lvl == &level && !rhs) .map(|(lvl, rhs)| lvl == &level && !rhs)
.unwrap_or_default() .unwrap_or_default()
|| function_before
{ {
idents.insert(id.to_string(), true); idents.insert(id.to_string(), true);
} else if !global_objects.contains(&id.as_ref()) {
last_ident = Some(id.to_string());
}
}
} else if last_ident.is_some() && !token.matches_punct(Punct::OpenParen) {
idents.entry(last_ident.unwrap()).or_default();
last_ident = None;
} else { } else {
last_ident = None; idents.entry(id.to_string()).or_default();
}
}
}
_ => {}
} }
} }
_ => break, _ => break,
}; };
period_before = token.matches_punct(Punct::Period); period_before = token.matches_punct(Punct::Period);
function_before = matches!(&token, Token::Keyword(Keyword::Function(_)));
} }
if state != 3 { if state != 3 {