Module refinery.units.formats.office.xlmdeobf

Expand source code Browse git
from __future__ import annotations

from refinery.lib.types import Param
from refinery.units.formats import Arg, Unit


class xlmdeobf(Unit):
    """
    Deobfuscates Excel v4.0 (XLM) macros from XLS, XLSM, and XLSB documents. By default, the
    program is emulated as the workbook stores it, and every cell the run executes is listed in
    the order it ran. An extraction lists the macrosheet cells instead: every statically
    computable formula is folded into the value it computes, and the cells that no formula or
    entry point leads to are left out.
    """
    @classmethod
    def handles(cls, data) -> bool | None:
        from refinery.lib.id import Fmt, get_microsoft_format, get_office_xml_type
        if get_microsoft_format(data) == Fmt.XLS:
            return True
        if get_office_xml_type(data) == Fmt.XLSX:
            return True

    def __init__(
        self,
        extract_only: Param[bool, Arg.Switch(
            '-x', help=(
                'List the macrosheet cells instead of emulating the program, with every '
                'statically computable formula folded and the cells nothing leads to left out.'
            )
        )] = False,
        sort_formulas: Param[bool, Arg.Switch(
            '-s', '--sort-formulas',
            help='Sort extracted formulas based on their cell address (implies -x).',
        )] = False,
        day: Param[int, Arg.Number(
            '-d',
            '--day',
            help='Specify the day of month',
        )] = -1,
        output_formula_format: Param[str, Arg.String(
            '-O', '--output-format',
            metavar='FMT',
            help=(
                'Specify the format for output formulas '
                '(using [[CELL-ADDR]], [[INT-FORMULA]], and [[STATUS]])'
            ),
        )] = 'CELL:[[CELL-ADDR]], [[STATUS]], [[INT-FORMULA]]',
        extract_formula_format: Param[str, Arg.String(
            '-E', '--extract-format',
            metavar='FMT',
            help=(
                'Specify the format for extracted formulas '
                '(using [[CELL-ADDR]], [[CELL-FORMULA]], and [[CELL-VALUE]])'
            ),
        )] = 'CELL:[[CELL-ADDR]], [[CELL-FORMULA]], [[CELL-VALUE]]',
        no_indent: Param[bool, Arg.Switch(
            '-I', '--no-indent',
            help='Do not show indent before formulas',
        )] = False,
        start_point: Param[str, Arg.String(
            '-c', '--start-point',
            help='Start interpretation from a specific cell address',
            metavar='CELL',
        )] = '',
        output_level: Param[int, Arg.Number(
            '-o',
            '--output-level',
            help=(
                'Set the level of details to be shown '
                '(0:all commands, 1: commands no jump 2:important '
                'commands 3:strings in important commands).'
            ),
        )] = 0,
        timeout: Param[int, Arg.Number(
            '-t',
            '--timeout',
            help=(
                'Stop emulation after N seconds '
                '(0: not interruption N>0: stop emulation after N seconds)'
            ),
        )] = 0,
    ):
        extract_only = sort_formulas or extract_only
        self.superinit(super(), **vars())

    def process(self, data: bytearray):
        from refinery.lib.excel.common import column_letters
        from refinery.lib.excel.formula import synthesize_formula
        from refinery.lib.scripts.xlm import XlmEngine, XlmView, deobfuscate
        from refinery.lib.scripts.xlm.trace import visible_steps

        view = XlmView(data)
        lines: list[str] = []
        if self.args.extract_only:
            deobfuscate(view, self.args.start_point)
            for macrosheet in view.macrosheets():
                lines.append(F'SHEET: {macrosheet.name}, {macrosheet.kind.name.lower()}')
                for cell in macrosheet.listing(self.args.sort_formulas):
                    formula = (
                        F'={synthesize_formula(cell.formula)}'
                        if cell.formula is not None
                        else 'None'
                    )
                    line = self.args.extract_formula_format
                    line = line.replace('[[CELL-ADDR]]', F'{column_letters(cell.col)}{cell.row}')
                    line = line.replace('[[CELL-FORMULA]]', formula)
                    line = line.replace('[[CELL-VALUE]]', str(cell.value))
                    lines.append(line)
        else:
            engine = XlmEngine(
                view,
                output_level=self.args.output_level,
                day=self.args.day,
                timeout=self.args.timeout,
            )
            for step in visible_steps(engine.run(self.args.start_point), self.args.output_level):
                formula = step.text
                if not self.args.no_indent:
                    formula = '\t' * step.indent + formula
                address = F'{column_letters(step.col)}{step.row}'
                line = self.args.output_formula_format
                line = line.replace('[[CELL-ADDR]]', F'{address:10}')
                line = line.replace('[[STATUS]]', F'{step.status.name:20}')
                line = line.replace('[[INT-FORMULA]]', formula)
                lines.append(line)
        return '\n'.join(lines).encode(self.codec)

Classes

class xlmdeobf (extract_only=False, sort_formulas=False, day=-1, output_formula_format='CELL:[[CELL-ADDR]], [[STATUS]], [[INT-FORMULA]]', extract_formula_format='CELL:[[CELL-ADDR]], [[CELL-FORMULA]], [[CELL-VALUE]]', no_indent=False, start_point='', output_level=0, timeout=0)

Deobfuscates Excel v4.0 (XLM) macros from XLS, XLSM, and XLSB documents. By default, the program is emulated as the workbook stores it, and every cell the run executes is listed in the order it ran. An extraction lists the macrosheet cells instead: every statically computable formula is folded into the value it computes, and the cells that no formula or entry point leads to are left out.

Expand source code Browse git
class xlmdeobf(Unit):
    """
    Deobfuscates Excel v4.0 (XLM) macros from XLS, XLSM, and XLSB documents. By default, the
    program is emulated as the workbook stores it, and every cell the run executes is listed in
    the order it ran. An extraction lists the macrosheet cells instead: every statically
    computable formula is folded into the value it computes, and the cells that no formula or
    entry point leads to are left out.
    """
    @classmethod
    def handles(cls, data) -> bool | None:
        from refinery.lib.id import Fmt, get_microsoft_format, get_office_xml_type
        if get_microsoft_format(data) == Fmt.XLS:
            return True
        if get_office_xml_type(data) == Fmt.XLSX:
            return True

    def __init__(
        self,
        extract_only: Param[bool, Arg.Switch(
            '-x', help=(
                'List the macrosheet cells instead of emulating the program, with every '
                'statically computable formula folded and the cells nothing leads to left out.'
            )
        )] = False,
        sort_formulas: Param[bool, Arg.Switch(
            '-s', '--sort-formulas',
            help='Sort extracted formulas based on their cell address (implies -x).',
        )] = False,
        day: Param[int, Arg.Number(
            '-d',
            '--day',
            help='Specify the day of month',
        )] = -1,
        output_formula_format: Param[str, Arg.String(
            '-O', '--output-format',
            metavar='FMT',
            help=(
                'Specify the format for output formulas '
                '(using [[CELL-ADDR]], [[INT-FORMULA]], and [[STATUS]])'
            ),
        )] = 'CELL:[[CELL-ADDR]], [[STATUS]], [[INT-FORMULA]]',
        extract_formula_format: Param[str, Arg.String(
            '-E', '--extract-format',
            metavar='FMT',
            help=(
                'Specify the format for extracted formulas '
                '(using [[CELL-ADDR]], [[CELL-FORMULA]], and [[CELL-VALUE]])'
            ),
        )] = 'CELL:[[CELL-ADDR]], [[CELL-FORMULA]], [[CELL-VALUE]]',
        no_indent: Param[bool, Arg.Switch(
            '-I', '--no-indent',
            help='Do not show indent before formulas',
        )] = False,
        start_point: Param[str, Arg.String(
            '-c', '--start-point',
            help='Start interpretation from a specific cell address',
            metavar='CELL',
        )] = '',
        output_level: Param[int, Arg.Number(
            '-o',
            '--output-level',
            help=(
                'Set the level of details to be shown '
                '(0:all commands, 1: commands no jump 2:important '
                'commands 3:strings in important commands).'
            ),
        )] = 0,
        timeout: Param[int, Arg.Number(
            '-t',
            '--timeout',
            help=(
                'Stop emulation after N seconds '
                '(0: not interruption N>0: stop emulation after N seconds)'
            ),
        )] = 0,
    ):
        extract_only = sort_formulas or extract_only
        self.superinit(super(), **vars())

    def process(self, data: bytearray):
        from refinery.lib.excel.common import column_letters
        from refinery.lib.excel.formula import synthesize_formula
        from refinery.lib.scripts.xlm import XlmEngine, XlmView, deobfuscate
        from refinery.lib.scripts.xlm.trace import visible_steps

        view = XlmView(data)
        lines: list[str] = []
        if self.args.extract_only:
            deobfuscate(view, self.args.start_point)
            for macrosheet in view.macrosheets():
                lines.append(F'SHEET: {macrosheet.name}, {macrosheet.kind.name.lower()}')
                for cell in macrosheet.listing(self.args.sort_formulas):
                    formula = (
                        F'={synthesize_formula(cell.formula)}'
                        if cell.formula is not None
                        else 'None'
                    )
                    line = self.args.extract_formula_format
                    line = line.replace('[[CELL-ADDR]]', F'{column_letters(cell.col)}{cell.row}')
                    line = line.replace('[[CELL-FORMULA]]', formula)
                    line = line.replace('[[CELL-VALUE]]', str(cell.value))
                    lines.append(line)
        else:
            engine = XlmEngine(
                view,
                output_level=self.args.output_level,
                day=self.args.day,
                timeout=self.args.timeout,
            )
            for step in visible_steps(engine.run(self.args.start_point), self.args.output_level):
                formula = step.text
                if not self.args.no_indent:
                    formula = '\t' * step.indent + formula
                address = F'{column_letters(step.col)}{step.row}'
                line = self.args.output_formula_format
                line = line.replace('[[CELL-ADDR]]', F'{address:10}')
                line = line.replace('[[STATUS]]', F'{step.status.name:20}')
                line = line.replace('[[INT-FORMULA]]', formula)
                lines.append(line)
        return '\n'.join(lines).encode(self.codec)

Ancestors

Subclasses

Class variables

var reverse

The type of the None singleton.

Inherited members