Module refinery.lib.scripts.js.deobfuscation.options
Caller-supplied options controlling JavaScript deobfuscation.
Expand source code Browse git
"""
Caller-supplied options controlling JavaScript deobfuscation.
"""
from __future__ import annotations
from dataclasses import dataclass
from fnmatch import fnmatchcase
@dataclass(frozen=True)
class DeobfuscationOptions:
"""
Options that steer JavaScript deobfuscation. *module* selects the execution model the input — and
therefore the deobfuscated output — is assumed to run under, which decides whether a top-level
declaration reaches the global object:
- Script model (default, `module=False`): a browser `<script>`, a Windows Script Host `.js`, or
any classic global script. A top-level `var`/`function` becomes a property of the global object,
so a global declaration produced by indirect `eval`, a string timer, or the `Function`
constructor may be inlined as a plain top-level declaration without changing meaning.
- Module model (`module=True`): an ES module or a CommonJS file run as `node file.js`. A top-level
declaration is scoped to the module and never reaches the global object. Indirect eval and string
timers still run in the global scope, so inlining a global declaration they produce into a plain
top-level declaration would silently move it out of the global object; such inlinings are
therefore declined to preserve semantics.
*entrypoints* holds wildcard patterns naming top-level functions that a host invokes by name — a JXA
`run`, a Windows Script Host handler, a browser event target. Such a function has no caller inside
the file, so reachability computed over the file alone judges it dead and removes it, together with
everything only it reached. Which names a host calls is not knowable from the file, so the analyst
supplies them; each is matched case-sensitively, because JavaScript identifiers are.
"""
module: bool = False
entrypoints: tuple[str, ...] = ()
def names_entrypoint(self, name: str) -> bool:
"""
Whether *name* matches one of the entrypoint patterns.
"""
return any(fnmatchcase(name, pattern) for pattern in self.entrypoints)
def module_execution(options: object | None) -> bool:
"""
Whether *options* selects the module execution model, under which a top-level binding is scoped to
the module and never reaches the global object. Any value that is not a `DeobfuscationOptions` — a
transformer run standalone, or with no options attached — defaults to the script model.
"""
return isinstance(options, DeobfuscationOptions) and options.module
def is_host_entrypoint(options: object | None, name: str) -> bool:
"""
Whether *name* is a top-level function the analyst declared a host calls by name, so removing it
would delete code that is reachable from outside the file. Any value that is not a
`DeobfuscationOptions` names no entrypoints, which is the behavior of every caller that supplies
none: reachability is then decided by the file alone, as before.
"""
return isinstance(options, DeobfuscationOptions) and options.names_entrypoint(name)
Functions
def module_execution(options)-
Whether options selects the module execution model, under which a top-level binding is scoped to the module and never reaches the global object. Any value that is not a
DeobfuscationOptions— a transformer run standalone, or with no options attached — defaults to the script model.Expand source code Browse git
def module_execution(options: object | None) -> bool: """ Whether *options* selects the module execution model, under which a top-level binding is scoped to the module and never reaches the global object. Any value that is not a `DeobfuscationOptions` — a transformer run standalone, or with no options attached — defaults to the script model. """ return isinstance(options, DeobfuscationOptions) and options.module def is_host_entrypoint(options, name)-
Whether name is a top-level function the analyst declared a host calls by name, so removing it would delete code that is reachable from outside the file. Any value that is not a
DeobfuscationOptionsnames no entrypoints, which is the behavior of every caller that supplies none: reachability is then decided by the file alone, as before.Expand source code Browse git
def is_host_entrypoint(options: object | None, name: str) -> bool: """ Whether *name* is a top-level function the analyst declared a host calls by name, so removing it would delete code that is reachable from outside the file. Any value that is not a `DeobfuscationOptions` names no entrypoints, which is the behavior of every caller that supplies none: reachability is then decided by the file alone, as before. """ return isinstance(options, DeobfuscationOptions) and options.names_entrypoint(name)
Classes
class DeobfuscationOptions (module=False, entrypoints=())-
Options that steer JavaScript deobfuscation. module selects the execution model the input — and therefore the deobfuscated output — is assumed to run under, which decides whether a top-level declaration reaches the global object:
-
Script model (default,
module=False): a browser<script>, a Windows Script Host.js, or any classic global script. A top-levelvar/functionbecomes a property of the global object, so a global declaration produced by indirecteval, a string timer, or theFunctionconstructor may be inlined as a plain top-level declaration without changing meaning. -
Module model (
module=True): an ES module or a CommonJS file run asnode file.js. A top-level declaration is scoped to the module and never reaches the global object. Indirect eval and string timers still run in the global scope, so inlining a global declaration they produce into a plain top-level declaration would silently move it out of the global object; such inlinings are therefore declined to preserve semantics.
entrypoints holds wildcard patterns naming top-level functions that a host invokes by name — a JXA
run, a Windows Script Host handler, a browser event target. Such a function has no caller inside the file, so reachability computed over the file alone judges it dead and removes it, together with everything only it reached. Which names a host calls is not knowable from the file, so the analyst supplies them; each is matched case-sensitively, because JavaScript identifiers are.Expand source code Browse git
@dataclass(frozen=True) class DeobfuscationOptions: """ Options that steer JavaScript deobfuscation. *module* selects the execution model the input — and therefore the deobfuscated output — is assumed to run under, which decides whether a top-level declaration reaches the global object: - Script model (default, `module=False`): a browser `<script>`, a Windows Script Host `.js`, or any classic global script. A top-level `var`/`function` becomes a property of the global object, so a global declaration produced by indirect `eval`, a string timer, or the `Function` constructor may be inlined as a plain top-level declaration without changing meaning. - Module model (`module=True`): an ES module or a CommonJS file run as `node file.js`. A top-level declaration is scoped to the module and never reaches the global object. Indirect eval and string timers still run in the global scope, so inlining a global declaration they produce into a plain top-level declaration would silently move it out of the global object; such inlinings are therefore declined to preserve semantics. *entrypoints* holds wildcard patterns naming top-level functions that a host invokes by name — a JXA `run`, a Windows Script Host handler, a browser event target. Such a function has no caller inside the file, so reachability computed over the file alone judges it dead and removes it, together with everything only it reached. Which names a host calls is not knowable from the file, so the analyst supplies them; each is matched case-sensitively, because JavaScript identifiers are. """ module: bool = False entrypoints: tuple[str, ...] = () def names_entrypoint(self, name: str) -> bool: """ Whether *name* matches one of the entrypoint patterns. """ return any(fnmatchcase(name, pattern) for pattern in self.entrypoints)Instance variables
var module-
The type of the None singleton.
var entrypoints-
The type of the None singleton.
Methods
def names_entrypoint(self, name)-
Whether name matches one of the entrypoint patterns.
Expand source code Browse git
def names_entrypoint(self, name: str) -> bool: """ Whether *name* matches one of the entrypoint patterns. """ return any(fnmatchcase(name, pattern) for pattern in self.entrypoints)
-