Module refinery.lib.scripts.js.deobfuscation.cff
Control-flow flattening recovery transforms.
Expand source code Browse git
"""
Control-flow flattening recovery transforms.
"""
from __future__ import annotations
from refinery.lib.scripts.js.deobfuscation.cff.sequential import JsControlFlowUnflattening
from refinery.lib.scripts.js.deobfuscation.cff.statemachine import JsGeneratorCFFUnflattening
__all__ = [
'JsControlFlowUnflattening',
'JsGeneratorCFFUnflattening',
]
Sub-modules
refinery.lib.scripts.js.deobfuscation.cff.sequential-
Recover sequential code from control-flow-flattened dispatchers …
refinery.lib.scripts.js.deobfuscation.cff.statemachine-
Recover original code from generator-based state-machine CFF dispatchers …
Classes
class JsControlFlowUnflattening-
Detect and recover CFF dispatchers in function bodies and script-level code.
Expand source code Browse git
class JsControlFlowUnflattening(BodyProcessingTransformer): """ Detect and recover CFF dispatchers in function bodies and script-level code. """ def _process_body(self, parent: Node, body: list[Statement]) -> None: i = 0 while i < len(body): stmt = body[i] if not isinstance(stmt, JsWhileStatement): i += 1 continue match = _match_dispatcher(stmt) if match is None: i += 1 continue order_info = _find_order_sequence(body, i, match.order_var, match.counter_var) if order_info is None: i += 1 continue if not all(label in match.case_map for label in order_info.order_sequence): i += 1 continue recovered: list[Statement] = [] for j in range(order_info.first_init_idx, i): if j not in order_info.init_indices: recovered.append(body[j]) for label in order_info.order_sequence: recovered.extend(match.case_map[label]) remove_start = order_info.first_init_idx remove_end = i replacement = body[:remove_start] + recovered + body[remove_end + 1:] self._replace_body(parent, body, replacement) i = remove_start + len(recovered)Ancestors
class JsGeneratorCFFUnflattening-
Recover original code from generator-based state-machine CFF dispatchers. Handles the pattern where a function body is replaced with a generator function containing a while/switch state machine driven by multiple state variables.
Expand source code Browse git
class JsGeneratorCFFUnflattening(BodyProcessingTransformer): """ Recover original code from generator-based state-machine CFF dispatchers. Handles the pattern where a function body is replaced with a generator function containing a while/switch state machine driven by multiple state variables. """ def _process_body(self, parent: Node, body: list[Statement]) -> None: is_script = isinstance(parent, JsScript) i = 0 while i < len(body): match = _match_generator_cff(body, i) if match is None: i += 1 continue machine = _extract_state_blocks(match) if machine is None: i += 1 continue result = _execute_machine(machine, match) if result is None: i += 1 continue recovered, outer_state = result if match.arg_var_name is not None: recovered = _resolve_shared_wrappers(recovered, machine, match, outer_state) if match.scope_default_props: recovered = _emit_scope_namespace_declarations(match) + recovered if match.arg_params: recovered = _emit_arg_param_declarations(match) + recovered if is_script: recovered = self._sanitize_for_script_scope(recovered) if recovered is None: i += 1 continue for s in recovered: s.parent = parent start = match.gen_decl_index end = match.scaffolding_end replacement = body[:start] + recovered + body[end + 1:] self._replace_body(parent, body, replacement) i = start + len(recovered) @staticmethod def _sanitize_for_script_scope(stmts: list[Statement]) -> list[Statement] | None: for stmt in stmts[:-1] if stmts else (): if isinstance(stmt, JsReturnStatement): return None if stmts and isinstance(stmts[-1], JsReturnStatement): last = stmts[-1] if last.argument is not None: stmts = stmts[:-1] + [JsExpressionStatement(expression=last.argument)] else: stmts = stmts[:-1] return stmtsAncestors