Module refinery.lib.scripts.ps1.deobfuscation

PowerShell AST deobfuscation transforms.

Expand source code Browse git
"""
PowerShell AST deobfuscation transforms.
"""
from __future__ import annotations

from refinery.lib.scripts.pipeline import DeobfuscationPipeline, TransformerGroup
from refinery.lib.scripts.ps1.deobfuscation.aliases import Ps1AliasInlining
from refinery.lib.scripts.ps1.deobfuscation.constants import (
    Ps1ConstantInlining,
    Ps1NullVariableInlining,
)
from refinery.lib.scripts.ps1.deobfuscation.deadcode import Ps1DeadCodeElimination
from refinery.lib.scripts.ps1.deobfuscation.emulator import Ps1ForEachPipeline, Ps1FunctionEvaluator
from refinery.lib.scripts.ps1.deobfuscation.expandable import Ps1ExpandableStringHoist
from refinery.lib.scripts.ps1.deobfuscation.folding import Ps1ConstantFolding
from refinery.lib.scripts.ps1.deobfuscation.iexinline import Ps1IexInlining
from refinery.lib.scripts.ps1.deobfuscation.rename import Ps1VariableRenaming
from refinery.lib.scripts.ps1.deobfuscation.securestring import Ps1SecureStringDecryptor
from refinery.lib.scripts.ps1.deobfuscation.simplify import Ps1Simplifications
from refinery.lib.scripts.ps1.deobfuscation.typecast import Ps1TypeCasts
from refinery.lib.scripts.ps1.deobfuscation.typenames import Ps1TypeSystemSimplifications
from refinery.lib.scripts.ps1.deobfuscation.unflatten import Ps1ControlFlowDeflattening
from refinery.lib.scripts.ps1.deobfuscation.unused import (
    Ps1JunkStatementRemoval,
    Ps1UnusedVariableRemoval,
)
from refinery.lib.scripts.ps1.deobfuscation.wildcards import Ps1WildcardResolution
from refinery.lib.scripts.ps1.model import Ps1Script

_folds = (
    Ps1ConstantFolding,
    Ps1DeadCodeElimination,
    Ps1ControlFlowDeflattening,
    Ps1ConstantInlining,
    Ps1ExpandableStringHoist,
    Ps1TypeCasts,
)

_cleanup = (
    Ps1NullVariableInlining,
    Ps1UnusedVariableRemoval,
    Ps1JunkStatementRemoval,
)

_fold_base = TransformerGroup('fold', *_folds)
_fold_full = TransformerGroup('fold', *_folds, *_cleanup)

_emulate = TransformerGroup(
    'emulate',
    Ps1ForEachPipeline,
    Ps1FunctionEvaluator,
)

_normalize = TransformerGroup(
    'normalize',
    Ps1Simplifications,
    Ps1AliasInlining,
    Ps1WildcardResolution,
    Ps1TypeSystemSimplifications,
)

_finalize = TransformerGroup(
    'finalize',
    Ps1SecureStringDecryptor,
    Ps1IexInlining,
)

_cosmetic = TransformerGroup(
    'cosmetic',
    Ps1VariableRenaming,
)

_DEPENDENCIES = {
    'fold'     : {'normalize'},
    'emulate'  : {'fold'},
    'finalize' : {'emulate'},
}

_INVALIDATORS = {
    'fold': {'normalize', 'emulate', 'finalize'},
}

_phase1 = DeobfuscationPipeline(
    [_normalize, _fold_base, _emulate, _finalize],
    dependencies=_DEPENDENCIES,
    invalidators=_INVALIDATORS,
)

_phase2 = DeobfuscationPipeline(
    [_normalize, _fold_full, _emulate, _finalize, _cosmetic],
    dependencies={**_DEPENDENCIES, 'cosmetic': {'finalize'}},
    invalidators=_INVALIDATORS,
)


def deobfuscate(ast: Ps1Script, max_steps: int = 0, remove_junk: bool = True) -> int:
    """
    Apply all available deobfuscators to the input. When `remove_junk` is `True`, a second pass
    removes unused variable assignments, uncalled function definitions, and side-effect-free
    expression statements.
    """
    steps = _phase1.run(ast, max_steps=max_steps)
    if not remove_junk:
        return steps
    if max_steps > 0:
        max_steps -= steps
    steps = _phase2.run(ast, max_steps=max_steps) + steps
    return steps

Sub-modules

refinery.lib.scripts.ps1.deobfuscation.aliases

Inline command aliases defined via Set-Alias / New-Alias.

refinery.lib.scripts.ps1.deobfuscation.constants

Inline constant variable references in PowerShell scripts.

refinery.lib.scripts.ps1.deobfuscation.data

Contains .NET type and PowerShell command database for deobfuscation. Generated via run-pwsh.ps1 from PowerShell 5.1 reflection data.

refinery.lib.scripts.ps1.deobfuscation.deadcode

Eliminate dead code from PowerShell scripts after constant folding.

refinery.lib.scripts.ps1.deobfuscation.emulator

Evaluate user-defined PowerShell functions called with constant arguments.

refinery.lib.scripts.ps1.deobfuscation.expandable

Hoist void subexpressions out of expandable strings, replacing the expandable string with a plain string literal of its text parts. The hoisted …

refinery.lib.scripts.ps1.deobfuscation.folding

PowerShell constant folding transforms.

refinery.lib.scripts.ps1.deobfuscation.helpers

Shared utilities for PowerShell deobfuscation transforms.

refinery.lib.scripts.ps1.deobfuscation.iexinline

Inline constant IEX (Invoke-Expression) and [scriptblock]::Create() calls by parsing the string argument …

refinery.lib.scripts.ps1.deobfuscation.rename

Rename obfuscated variable names to short sequential identifiers.

refinery.lib.scripts.ps1.deobfuscation.securestring

PowerShell SecureString decryption transformer.

refinery.lib.scripts.ps1.deobfuscation.simplify

PowerShell syntax normalization transforms.

refinery.lib.scripts.ps1.deobfuscation.typecast

PowerShell type cast simplification transforms.

refinery.lib.scripts.ps1.deobfuscation.typenames

.NET type system utilities for PowerShell deobfuscation.

refinery.lib.scripts.ps1.deobfuscation.unflatten

Recover original control flow from control-flow-flattened PowerShell scripts …

refinery.lib.scripts.ps1.deobfuscation.unused

Remove unused variable assignments and junk expression statements.

refinery.lib.scripts.ps1.deobfuscation.wildcards

Resolve wildcard-based obfuscation patterns in PowerShell scripts …

Functions

def deobfuscate(ast, max_steps=0, remove_junk=True)

Apply all available deobfuscators to the input. When remove_junk is True, a second pass removes unused variable assignments, uncalled function definitions, and side-effect-free expression statements.

Expand source code Browse git
def deobfuscate(ast: Ps1Script, max_steps: int = 0, remove_junk: bool = True) -> int:
    """
    Apply all available deobfuscators to the input. When `remove_junk` is `True`, a second pass
    removes unused variable assignments, uncalled function definitions, and side-effect-free
    expression statements.
    """
    steps = _phase1.run(ast, max_steps=max_steps)
    if not remove_junk:
        return steps
    if max_steps > 0:
        max_steps -= steps
    steps = _phase2.run(ast, max_steps=max_steps) + steps
    return steps