Module refinery.units.formats.archive.innopwd

Expand source code Browse git
from __future__ import annotations

import functools

from refinery.lib.inno.archive import InnoArchive, is_inno_setup
from refinery.lib.inno.emulation import (
    NewEmulationError,
    NewFunctionCall,
    NewInstruction,
    NewMutex,
    NewPassword,
    NewScriptError,
)
from refinery.lib.inno.emulator import IFPSEmulatorConfig, InnoSetupEmulator
from refinery.lib.inno.ifps import IFPSFile
from refinery.units import Unit


class innopwd(Unit):
    """
    This unit emulates an InnoSetup installer in an attempt to determine the installer password.

    This works only when the password is contained within the script, but several malware samples
    are known to use this technique.
    """
    def process(self, data: bytearray):
        if data.startswith(IFPSFile.Magic):
            inno = IFPSFile.Parse(data)
            self.log_info('running in script-only mode; cannot check passwords')
            password_test = None
        else:
            inno = InnoArchive(data, self)
            file = inno.get_encrypted_sample()
            if file is None:
                self.log_info('the archive is not password-protected, password is empty')
                return None
            password_test = functools.partial(inno.check_password, file)
            assert file.crypto
            self.log_info('password type:', file.crypto.PasswordType.name)
            self.log_info('password test:', file.crypto.PasswordTest)
            self.log_info('password seed:', file.crypto.PasswordSeed)

        info = self.log_info()
        dbug = self.log_debug()
        emulator = InnoSetupEmulator(inno, IFPSEmulatorConfig(
            log_mutexes=info,
            log_passwords=True,
            log_calls=info,
            log_opcodes=dbug,
        ))
        function = None

        for event in emulator.emulate_installation(password_test=password_test):
            if isinstance(event, NewInstruction):
                if not function or function.name != event.function.name:
                    function = event.function
                    self.log_debug(repr(function))
                self.log_debug(F'\x20\x20{event.offset:#08x} {event.instruction!s}')
                continue
            if isinstance(event, NewFunctionCall):
                self.log_info(F'calling {event!s}')
                continue
            if isinstance(event, (NewScriptError, NewEmulationError)):
                self.log_warn(str(event))
                continue
            if isinstance(event, NewMutex):
                self.log_info(F'mutex registered: {event!s}')
                continue
            if isinstance(event, NewPassword):
                if password_test is not None and not password_test(event):
                    self.log_info('discarding password:', event)
                    continue
                yield event.encode(self.codec)
                if password_test is not None:
                    self.log_info('aborting emulation after validating password')
                    return

    @classmethod
    def handles(cls, data):
        return is_inno_setup(data)

Classes

class innopwd

This unit emulates an InnoSetup installer in an attempt to determine the installer password.

This works only when the password is contained within the script, but several malware samples are known to use this technique.

Expand source code Browse git
class innopwd(Unit):
    """
    This unit emulates an InnoSetup installer in an attempt to determine the installer password.

    This works only when the password is contained within the script, but several malware samples
    are known to use this technique.
    """
    def process(self, data: bytearray):
        if data.startswith(IFPSFile.Magic):
            inno = IFPSFile.Parse(data)
            self.log_info('running in script-only mode; cannot check passwords')
            password_test = None
        else:
            inno = InnoArchive(data, self)
            file = inno.get_encrypted_sample()
            if file is None:
                self.log_info('the archive is not password-protected, password is empty')
                return None
            password_test = functools.partial(inno.check_password, file)
            assert file.crypto
            self.log_info('password type:', file.crypto.PasswordType.name)
            self.log_info('password test:', file.crypto.PasswordTest)
            self.log_info('password seed:', file.crypto.PasswordSeed)

        info = self.log_info()
        dbug = self.log_debug()
        emulator = InnoSetupEmulator(inno, IFPSEmulatorConfig(
            log_mutexes=info,
            log_passwords=True,
            log_calls=info,
            log_opcodes=dbug,
        ))
        function = None

        for event in emulator.emulate_installation(password_test=password_test):
            if isinstance(event, NewInstruction):
                if not function or function.name != event.function.name:
                    function = event.function
                    self.log_debug(repr(function))
                self.log_debug(F'\x20\x20{event.offset:#08x} {event.instruction!s}')
                continue
            if isinstance(event, NewFunctionCall):
                self.log_info(F'calling {event!s}')
                continue
            if isinstance(event, (NewScriptError, NewEmulationError)):
                self.log_warn(str(event))
                continue
            if isinstance(event, NewMutex):
                self.log_info(F'mutex registered: {event!s}')
                continue
            if isinstance(event, NewPassword):
                if password_test is not None and not password_test(event):
                    self.log_info('discarding password:', event)
                    continue
                yield event.encode(self.codec)
                if password_test is not None:
                    self.log_info('aborting emulation after validating password')
                    return

    @classmethod
    def handles(cls, data):
        return is_inno_setup(data)

Ancestors

Subclasses

Class variables

var reverse

The type of the None singleton.

Inherited members