예제 #1
0
class MRUListExStringAndShellItemListPlugin(
    interface.KeyPlugin, MRUListExPluginMixin):
  """Windows Registry plugin to parse a string and shell item list MRUListEx."""

  NAME = 'winreg_mrulistex_string_and_shell_item_list'
  DESCRIPTION = u'Parser for Most Recently Used (MRU) Registry data.'

  REG_TYPE = 'any'
  REG_KEYS = frozenset([
      (u'\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\'
       u'LastVisitedPidlMRU')])

  _STRING_AND_SHELL_ITEM_LIST_STRUCT = construct.Struct(
      'string_and_shell_item',
      construct.RepeatUntil(
          lambda obj, ctx: obj == '\x00\x00', construct.Field('string', 2)),
      construct.Anchor('shell_item_list'))

  def _ParseMRUListExEntryValue(
      self, parser_context, key, entry_index, entry_number, text_dict,
      codepage='cp1252', **unused_kwargs):
    """Parses the MRUListEx entry value.

    Args:
      parser_context: A parser context object (instance of ParserContext).
      key: the Registry key (instance of winreg.WinRegKey) that contains
           the MRUListEx value.
      entry_index: integer value representing the MRUListEx entry index.
      entry_number: integer value representing the entry number.
      text_dict: text dictionary object to append textual strings.
      codepage: Optional extended ASCII string codepage. The default is cp1252.
    """
    value_string = u''

    value = key.GetValue(u'{0:d}'.format(entry_number))
    if value is None:
      logging.debug(
          u'[{0:s}] Missing MRUListEx entry value: {1:d} in key: {2:s}.'.format(
              self.NAME, entry_number, key.path))

    elif not value.DataIsBinaryData():
      logging.debug((
          u'[{0:s}] Non-binary MRUListEx entry value: {1:d} in key: '
          u'{2:s}.').format(self.NAME, entry_number, key.path))

    elif value.data:
      value_struct = self._STRING_AND_SHELL_ITEM_LIST_STRUCT.parse(value.data)

      try:
        # The struct includes the end-of-string character that we need
        # to strip off.
        path = ''.join(value_struct.string).decode('utf16')[:-1]
      except UnicodeDecodeError as exception:
        logging.warning((
            u'[{0:s}] Unable to decode string MRUListEx entry value: {1:d} '
            u'in key: {2:s} with error: {3:s}').format(
                self.NAME, entry_number, key.path, exception))
        path = u''

      if path:
        shell_item_list_data = value.data[value_struct.shell_item_list:]
        if not shell_item_list_data:
          logging.debug((
              u'[{0:s}] Missing shell item in MRUListEx entry value: {1:d}'
              u'in key: {2:s}').format(self.NAME, entry_number, key.path))
          value_string = u'Path: {0:s}'.format(path)

        else:
          shell_items_parser = shell_items.ShellItemsParser(key.path)
          shell_items_parser.Parse(
              parser_context, shell_item_list_data, codepage=codepage)

          value_string = u'Path: {0:s}, Shell item list: [{1:s}]'.format(
              path, shell_items_parser.CopyToPath())

    value_text = u'Index: {0:d} [MRU Value {1:d}]'.format(
        entry_index + 1, entry_number)

    text_dict[value_text] = value_string

  def GetEntries(
      self, parser_context, key=None, registry_type=None, codepage='cp1252',
      **unused_kwargs):
    """Extract event objects from a Registry key containing a MRUListEx value.

    Args:
      parser_context: A parser context object (instance of ParserContext).
      key: Optional Registry key (instance of winreg.WinRegKey).
           The default is None.
      registry_type: Optional Registry type string. The default is None.
      codepage: Optional extended ASCII string codepage. The default is cp1252.
    """
    self._ParseMRUListExKey(
        parser_context, key, registry_type=registry_type, codepage=codepage)
예제 #2
0
    construct.ULInt32('id'),
    construct.ULInt32('attr_unknown_1'),
    construct.ULInt32('attr_unknown_2'),
    construct.ULInt32('attr_unknown_3'),
    # Ok, this is bad, but till now I have not understood how to distinguish
    # the different structs used. Actually the last ATTRIBUTE is different.
    # Usually we have 6 more bytes zeroed, not always aligned: otherwise,
    # if id >= 100, we have 4 more bytes: weird.
    construct.Optional(
        construct.Const(construct.Bytes('padding', 6), '\x00' * 6)),
    construct.If(lambda ctx: not ctx.padding and ctx.id >= 0x64,
                 construct.ULInt32('attr_unknown_4')),
    construct.ULInt32('size'),
    construct.If(lambda ctx: ctx.size > 0,
                 construct.Embed(VAULT_ATTRIBUTE_ENCRYPTED)),
    construct.Anchor('stream_end'))

VAULT_ATTRIBUTE_EXTRA = construct.Struct('VAULT_ATTRIBUTE_EXTRA',
                                         construct.ULInt32('id'),
                                         construct.ULInt32('attr_unknown_1'),
                                         construct.ULInt32('attr_unknown_2'),
                                         construct.Embed(SIZED_DATA))

VAULT_ATTRIBUTE_MAP_ENTRY = construct.Struct(
    'VAULT_ATTRIBUTE_MAP_ENTRY', construct.ULInt32('id'),
    construct.ULInt32('offset'), construct.ULInt32('attr_map_entry_unknown_1'),
    construct.Pointer(lambda ctx: ctx.offset, VAULT_ATTRIBUTE))

VAULT_VCRD = construct.Struct(
    'VAULT_VCRD', GUID('schema_guid'), construct.ULInt32('vcrd_unknown_1'),
    FILETIME('last_update'), construct.ULInt32('vcrd_unknown_2'),
예제 #3
0
class MRUListExStringAndShellItemListPlugin(BaseMRUListExPlugin):
  """Windows Registry plugin to parse a string and shell item list MRUListEx."""

  NAME = 'mrulistex_string_and_shell_item_list'
  DESCRIPTION = 'Parser for Most Recently Used (MRU) Registry data.'

  FILTERS = frozenset([
      interface.WindowsRegistryKeyPathFilter(
          'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\'
          'Explorer\\ComDlg32\\LastVisitedPidlMRU')])

  _STRING_AND_SHELL_ITEM_LIST_STRUCT = construct.Struct(
      'string_and_shell_item',
      construct.RepeatUntil(
          lambda obj, ctx: obj == b'\x00\x00', construct.Field('string', 2)),
      construct.Anchor('shell_item_list'))

  # pylint: disable=arguments-differ
  def _ParseMRUListExEntryValue(
      self, parser_mediator, registry_key, entry_index, entry_number,
      codepage='cp1252', **kwargs):
    """Parses the MRUListEx entry value.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      registry_key (dfwinreg.WinRegistryKey): Windows Registry key that contains
           the MRUListEx value.
      entry_index (int): MRUListEx entry index.
      entry_number (int): entry number.
      codepage (Optional[str]): extended ASCII string codepage.

    Returns:
      str: MRUList entry value.
    """
    value_string = ''

    value = registry_key.GetValueByName('{0:d}'.format(entry_number))
    if value is None:
      logger.debug(
          '[{0:s}] Missing MRUListEx entry value: {1:d} in key: {2:s}.'.format(
              self.NAME, entry_number, registry_key.path))

    elif not value.DataIsBinaryData():
      logger.debug((
          '[{0:s}] Non-binary MRUListEx entry value: {1:d} in key: '
          '{2:s}.').format(self.NAME, entry_number, registry_key.path))

    elif value.data:
      value_struct = self._STRING_AND_SHELL_ITEM_LIST_STRUCT.parse(value.data)

      try:
        # The struct includes the end-of-string character that we need
        # to strip off.
        path = b''.join(value_struct.string).decode('utf16')[:-1]
      except UnicodeDecodeError as exception:
        logger.warning((
            '[{0:s}] Unable to decode string MRUListEx entry value: {1:d} '
            'in key: {2:s} with error: {3!s}').format(
                self.NAME, entry_number, registry_key.path, exception))
        path = ''

      if path:
        shell_item_list_data = value.data[value_struct.shell_item_list:]
        if not shell_item_list_data:
          logger.debug((
              '[{0:s}] Missing shell item in MRUListEx entry value: {1:d}'
              'in key: {2:s}').format(
                  self.NAME, entry_number, registry_key.path))
          value_string = 'Path: {0:s}'.format(path)

        else:
          shell_items_parser = shell_items.ShellItemsParser(registry_key.path)
          shell_items_parser.ParseByteStream(
              parser_mediator, shell_item_list_data, codepage=codepage)

          value_string = 'Path: {0:s}, Shell item path: {1:s}'.format(
              path, shell_items_parser.CopyToPath())

    return value_string

  # pylint: disable=arguments-differ
  def ExtractEvents(
      self, parser_mediator, registry_key, codepage='cp1252', **kwargs):
    """Extracts events from a Windows Registry key.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      registry_key (dfwinreg.WinRegistryKey): Windows Registry key.
      codepage (Optional[str]): extended ASCII string codepage.
    """
    self._ParseMRUListExKey(parser_mediator, registry_key, codepage=codepage)
예제 #4
0
class MRUListExStringAndShellItemListPlugin(BaseMRUListExPlugin):
    """Windows Registry plugin to parse a string and shell item list MRUListEx."""

    NAME = u'mrulistex_string_and_shell_item_list'
    DESCRIPTION = u'Parser for Most Recently Used (MRU) Registry data.'

    FILTERS = frozenset([
        interface.WindowsRegistryKeyPathFilter(
            u'HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\'
            u'Explorer\\ComDlg32\\LastVisitedPidlMRU')
    ])

    _STRING_AND_SHELL_ITEM_LIST_STRUCT = construct.Struct(
        u'string_and_shell_item',
        construct.RepeatUntil(lambda obj, ctx: obj == b'\x00\x00',
                              construct.Field(u'string', 2)),
        construct.Anchor(u'shell_item_list'))

    def _ParseMRUListExEntryValue(self,
                                  parser_mediator,
                                  key,
                                  entry_index,
                                  entry_number,
                                  codepage=u'cp1252',
                                  **unused_kwargs):
        """Parses the MRUListEx entry value.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      key: the Registry key (instance of dfwinreg.WinRegistryKey) that contains
           the MRUListEx value.
      entry_index: integer value representing the MRUListEx entry index.
      entry_number: integer value representing the entry number.
      codepage: Optional extended ASCII string codepage.

    Returns:
      A string containing the value.
    """
        value_string = u''

        value = key.GetValueByName(u'{0:d}'.format(entry_number))
        if value is None:
            logging.debug(
                u'[{0:s}] Missing MRUListEx entry value: {1:d} in key: {2:s}.'.
                format(self.NAME, entry_number, key.path))

        elif not value.DataIsBinaryData():
            logging.debug(
                (u'[{0:s}] Non-binary MRUListEx entry value: {1:d} in key: '
                 u'{2:s}.').format(self.NAME, entry_number, key.path))

        elif value.data:
            value_struct = self._STRING_AND_SHELL_ITEM_LIST_STRUCT.parse(
                value.data)

            try:
                # The struct includes the end-of-string character that we need
                # to strip off.
                path = b''.join(value_struct.string).decode(u'utf16')[:-1]
            except UnicodeDecodeError as exception:
                logging.warning((
                    u'[{0:s}] Unable to decode string MRUListEx entry value: {1:d} '
                    u'in key: {2:s} with error: {3:s}').format(
                        self.NAME, entry_number, key.path, exception))
                path = u''

            if path:
                shell_item_list_data = value.data[value_struct.
                                                  shell_item_list:]
                if not shell_item_list_data:
                    logging.debug((
                        u'[{0:s}] Missing shell item in MRUListEx entry value: {1:d}'
                        u'in key: {2:s}').format(self.NAME, entry_number,
                                                 key.path))
                    value_string = u'Path: {0:s}'.format(path)

                else:
                    shell_items_parser = shell_items.ShellItemsParser(key.path)
                    shell_items_parser.ParseByteStream(parser_mediator,
                                                       shell_item_list_data,
                                                       codepage=codepage)

                    value_string = u'Path: {0:s}, Shell item path: {1:s}'.format(
                        path, shell_items_parser.CopyToPath())

        return value_string

    def GetEntries(self,
                   parser_mediator,
                   registry_key,
                   codepage=u'cp1252',
                   **kwargs):
        """Extract event objects from a Registry key containing a MRUListEx value.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      registry_key: A Windows Registry key (instance of
                    dfwinreg.WinRegistryKey).
      codepage: Optional extended ASCII string codepage.
    """
        self._ParseMRUListExKey(parser_mediator,
                                registry_key,
                                codepage=codepage)
예제 #5
0
_POINTER = construct.Pointer
_REPEAT = construct.RepeatUntil
_INT = construct.ULInt32('integer')
_INT64 = construct.ULInt64('integer64')

_BOOKMARK_DATA = construct.Struct(
    'bookmark_data',
    construct.String('magic', 4),
    construct.ULInt32('length'),
    construct.UBInt32('version'),
    construct.ULInt32('offset'),  # offset to "FirstToC Offset"
    _POINTER(
        lambda ctx: ctx.offset,
        construct.Struct(
            'ftoc_offset',
            construct.Anchor('abs_offset'),
            construct.ULInt32('offset'),
            _POINTER(
                lambda ctx: ctx.abs_offset + ctx.offset,
                _REPEAT(
                    lambda obj, ctx: obj.offset == 0x00000000,
                    construct.Struct(
                        'toc',
                        construct.ULInt32('length'),
                        construct.SLInt16('type'),
                        construct.UBInt16('flag'),
                        construct.ULInt32('level'),
                        construct.ULInt32(
                            'offset'),  # offset to next ToC (0 if none)
                        construct.ULInt32('count'),
                        _ARRAY(
예제 #6
0
class MRUListExStringAndShellItemPlugin(interface.KeyPlugin,
                                        MRUListExPluginMixin):
    """Windows Registry plugin to parse a string and shell item MRUListEx."""

    NAME = 'winreg_mrulistex_string_and_shell_item'
    DESCRIPTION = u'Parser for Most Recently Used (MRU) Registry data.'

    REG_TYPE = 'any'
    REG_KEYS = frozenset([
        u'\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RecentDocs'
    ])

    _STRING_AND_SHELL_ITEM_STRUCT = construct.Struct(
        'string_and_shell_item',
        construct.RepeatUntil(lambda obj, ctx: obj == '\x00\x00',
                              construct.Field('string', 2)),
        construct.Anchor('shell_item'))

    def _ParseMRUListExEntryValue(self,
                                  parser_mediator,
                                  key,
                                  entry_index,
                                  entry_number,
                                  codepage='cp1252',
                                  **unused_kwargs):
        """Parses the MRUListEx entry value.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      key: the Registry key (instance of winreg.WinRegKey) that contains
           the MRUListEx value.
      entry_index: integer value representing the MRUListEx entry index.
      entry_number: integer value representing the entry number.
      codepage: Optional extended ASCII string codepage. The default is cp1252.

    Returns:
      A string containing the value.
    """
        value_string = u''

        value = key.GetValue(u'{0:d}'.format(entry_number))
        if value is None:
            logging.debug(
                u'[{0:s}] Missing MRUListEx entry value: {1:d} in key: {2:s}.'.
                format(self.NAME, entry_number, key.path))

        elif not value.DataIsBinaryData():
            logging.debug(
                (u'[{0:s}] Non-binary MRUListEx entry value: {1:d} in key: '
                 u'{2:s}.').format(self.NAME, entry_number, key.path))

        elif value.data:
            value_struct = self._STRING_AND_SHELL_ITEM_STRUCT.parse(value.data)

            try:
                # The struct includes the end-of-string character that we need
                # to strip off.
                path = ''.join(value_struct.string).decode('utf16')[:-1]
            except UnicodeDecodeError as exception:
                logging.warning((
                    u'[{0:s}] Unable to decode string MRUListEx entry value: {1:d} '
                    u'in key: {2:s} with error: {3:s}').format(
                        self.NAME, entry_number, key.path, exception))
                path = u''

            if path:
                shell_item_list_data = value.data[value_struct.shell_item:]
                if not shell_item_list_data:
                    logging.debug((
                        u'[{0:s}] Missing shell item in MRUListEx entry value: {1:d}'
                        u'in key: {2:s}').format(self.NAME, entry_number,
                                                 key.path))
                    value_string = u'Path: {0:s}'.format(path)

                else:
                    shell_items_parser = shell_items.ShellItemsParser(key.path)
                    shell_items_parser.UpdateChainAndParse(
                        parser_mediator,
                        shell_item_list_data,
                        None,
                        codepage=codepage)

                    value_string = u'Path: {0:s}, Shell item: [{1:s}]'.format(
                        path, shell_items_parser.CopyToPath())

        return value_string

    def GetEntries(self,
                   parser_mediator,
                   key=None,
                   registry_type=None,
                   codepage='cp1252',
                   **kwargs):
        """Extract event objects from a Registry key containing a MRUListEx value.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      key: Optional Registry key (instance of winreg.WinRegKey).
           The default is None.
      registry_type: Optional Registry type string. The default is None.
      codepage: Optional extended ASCII string codepage. The default is cp1252.
    """
        self._ParseMRUListExKey(parser_mediator,
                                key,
                                registry_type=registry_type,
                                codepage=codepage)

        if key.name == u'RecentDocs':
            # For the RecentDocs MRUListEx we also need to parse its subkeys
            # since the Registry key path does not support wildcards yet.
            for subkey in key.GetSubkeys():
                self._ParseMRUListExKey(parser_mediator,
                                        subkey,
                                        registry_type=registry_type,
                                        codepage=codepage)
예제 #7
0
class RestorePointChangeLogFile(object):
    """Class that contains a Windows Restore Point change.log file."""

    SIGNATURE = 0xabcdef12

    _CHANGE_LOG_ENTRY = construct.Struct(
        u'restore_point_change_log_entry', construct.ULInt32(u'record_size'),
        construct.ULInt32(u'record_type'), construct.ULInt32(u'signature'),
        construct.ULInt32(u'entry_type'), construct.ULInt32(u'entry_flags'),
        construct.ULInt32(u'file_attribute_flags'),
        construct.ULInt64(u'sequence_number'), construct.Padding(32),
        construct.ULInt32(u'process_name_data_size'),
        construct.ULInt32(u'unknown1'),
        construct.RepeatUntil(lambda obj, ctx: obj == b'\x00\x00',
                              construct.Field(u'process_name', 2)),
        construct.Anchor(u'sub_record_data'))

    _FILE_HEADER = construct.Struct(u'restore_point_change_log_file_header',
                                    construct.ULInt32(u'record_size'),
                                    construct.ULInt32(u'record_type'),
                                    construct.ULInt32(u'signature'),
                                    construct.ULInt32(u'format_version'))

    _RECORD_HEADER = construct.Struct(
        u'restore_point_change_log_record_header',
        construct.ULInt32(u'record_size'), construct.ULInt32(u'record_type'))

    _UTF16_STREAM = construct.RepeatUntil(lambda obj, ctx: obj == b'\x00\x00',
                                          construct.Field(u'stream', 2))

    _VOLUME_PATH = construct.Struct(
        u'restore_point_change_log_volume_path',
        construct.ULInt32(u'record_size'), construct.ULInt32(u'record_type'),
        construct.RepeatUntil(lambda obj, ctx: obj == b'\x00\x00',
                              construct.Field(u'volume_path', 2)))

    LOG_ENTRY_FLAGS = {
        0x00000001: u'CHANGE_LOG_ENTRYFLAGS_TEMPPATH',
        0x00000002: u'CHANGE_LOG_ENTRYFLAGS_SECONDPATH',
        0x00000004: u'CHANGE_LOG_ENTRYFLAGS_ACLINFO',
        0x00000008: u'CHANGE_LOG_ENTRYFLAGS_DEBUGINFO',
        0x00000010: u'CHANGE_LOG_ENTRYFLAGS_SHORTNAME',
    }

    LOG_ENTRY_TYPES = {
        0x00000001: u'CHANGE_LOG_ENTRYTYPES_STREAMCHANGE',
        0x00000002: u'CHANGE_LOG_ENTRYTYPES_ACLCHANGE',
        0x00000004: u'CHANGE_LOG_ENTRYTYPES_ATTRCHANGE',
        0x00000008: u'CHANGE_LOG_ENTRYTYPES_STREAMOVERWRITE',
        0x00000010: u'CHANGE_LOG_ENTRYTYPES_FILEDELETE',
        0x00000020: u'CHANGE_LOG_ENTRYTYPES_FILECREATE',
        0x00000040: u'CHANGE_LOG_ENTRYTYPES_FILERENAME',
        0x00000080: u'CHANGE_LOG_ENTRYTYPES_DIRCREATE',
        0x00000100: u'CHANGE_LOG_ENTRYTYPES_DIRRENAME',
        0x00000200: u'CHANGE_LOG_ENTRYTYPES_DIRDELETE',
        0x00000400: u'CHANGE_LOG_ENTRYTYPES_MOUNTCREATE',
        0x00000800: u'CHANGE_LOG_ENTRYTYPES_MOUNTDELETE',
        0x00001000: u'CHANGE_LOG_ENTRYTYPES_VOLUMEERROR',
        0x00002000: u'CHANGE_LOG_ENTRYTYPES_STREAMCREATE',
        0x00010000: u'CHANGE_LOG_ENTRYTYPES_NOOPTIMIZE',
        0x00020000: u'CHANGE_LOG_ENTRYTYPES_ISDIR',
        0x00040000: u'CHANGE_LOG_ENTRYTYPES_ISNOTDIR',
        0x00080000: u'CHANGE_LOG_ENTRYTYPES_SIMULATEDELETE',
        0x00100000: u'CHANGE_LOG_ENTRYTYPES_INPRECREATE',
        0x00200000: u'CHANGE_LOG_ENTRYTYPES_OPENBYID',
    }

    _RECORD_TYPES = {
        0: u'RecordTypeLogHeader',
        1: u'RecordTypeLogEntry',
        2: u'RecordTypeVolumePath',
        3: u'RecordTypeFirstPath',
        4: u'RecordTypeSecondPath',
        5: u'RecordTypeTempPath',
        6: u'RecordTypeAclInline',
        7: u'RecordTypeAclFile',
        8: u'RecordTypeDebugInfo',
        9: u'RecordTypeShortName',
    }

    def __init__(self, debug=False):
        """Initializes the change.log file object.

    Args:
      debug: optional boolean value to indicate if debug information should
             be printed.
    """
        super(RestorePointChangeLogFile, self).__init__()
        self._debug = debug
        self._file_object = None
        self._file_object_opened_in_object = False
        self._file_size = 0

        self.entries = []
        self.volume_path = None

    def _ReadChangeLogEntries(self):
        """Reads the change log entries.

    Raises:
      IOError: if the change log entries cannot be read.
    """
        while self._file_object.tell() < self._file_size:
            change_log_entry = self._ReadChangeLogEntry()
            self.entries.append(change_log_entry)

    def _ReadChangeLogEntry(self):
        """Reads a change log entry.

    Returns:
      A change log entry (instance of ChangeLogEntry).

    Raises:
      IOError: if the change log entry cannot be read.
    """
        file_offset = self._file_object.tell()

        try:
            change_log_entry_struct = self._CHANGE_LOG_ENTRY.parse_stream(
                self._file_object)
        except construct.FieldError as exception:
            raise IOError(
                u'Unable to parse change log entry with error: {0:s}'.format(
                    exception))

        record_size = change_log_entry_struct.get(u'record_size')

        record_type = change_log_entry_struct.get(u'record_type')
        if record_type != 1:
            raise IOError(
                u'Unsupported record type: {0:d}'.format(record_type))

        signature = change_log_entry_struct.get(u'signature')
        if signature != self.SIGNATURE:
            raise IOError(u'Unsupported change.log file signature')

        change_log_entry = ChangeLogEntry()
        change_log_entry.entry_type = change_log_entry_struct.get(
            u'entry_type')
        change_log_entry.entry_flags = change_log_entry_struct.get(
            u'entry_flags')
        change_log_entry.file_attribute_flags = change_log_entry_struct.get(
            u'file_attribute_flags')
        change_log_entry.sequence_number = change_log_entry_struct.get(
            u'sequence_number')

        try:
            # The struct includes the end-of-string character that we need
            # to strip off.
            process_name = change_log_entry_struct.get(u'process_name')
            process_name = b''.join(process_name).decode(u'utf16')[:-1]
        except UnicodeDecodeError as exception:
            process_name = u''

        change_log_entry.process_name = process_name

        self._file_object.seek(file_offset, os.SEEK_SET)

        change_log_entry_record_data = self._file_object.read(record_size)

        if self._debug:
            print(u'Change log entry record data:')
            print(hexdump.Hexdump(change_log_entry_record_data))

        if self._debug:
            print(u'Record size\t\t\t\t\t\t\t\t: {0:d}'.format(record_size))
            print(u'Record type\t\t\t\t\t\t\t\t: {0:d} ({1:s})'.format(
                record_type, self._RECORD_TYPES.get(record_type, u'Unknown')))
            print(u'Signature\t\t\t\t\t\t\t\t: 0x{0:08x}'.format(signature))
            print(u'Entry type\t\t\t\t\t\t\t\t: 0x{0:08x}'.format(
                change_log_entry.entry_type))
            for flag, description in self.LOG_ENTRY_TYPES.items():
                if change_log_entry.entry_type & flag:
                    print(u'\t{0:s}'.format(description))
            print(u'')

            print(u'Entry flags\t\t\t\t\t\t\t\t: 0x{0:08x}'.format(
                change_log_entry.entry_flags))
            for flag, description in self.LOG_ENTRY_FLAGS.items():
                if change_log_entry.entry_flags & flag:
                    print(u'\t{0:s}'.format(description))
            print(u'')

            print(u'File attribute flags\t\t\t\t\t\t\t: 0x{0:08x}'.format(
                change_log_entry.file_attribute_flags))
            # TODO: print flags.

            print(u'Sequence number\t\t\t\t\t\t\t\t: {0:d}'.format(
                change_log_entry.sequence_number))
            print(u'Process name data size\t\t\t\t\t\t\t: 0x{0:08x}'.format(
                change_log_entry_struct.get(u'process_name_data_size')))
            print(u'Unknown1\t\t\t\t\t\t\t\t: 0x{0:08x}'.format(
                change_log_entry_struct.get(u'unknown1')))
            print(u'Process name\t\t\t\t\t\t\t\t: {0:s}'.format(
                change_log_entry.process_name))

        sub_record_data_offset = (change_log_entry_struct.sub_record_data -
                                  file_offset)
        sub_record_data_size = record_size - 4
        if self._debug:
            print(u'Sub record data offset\t\t\t\t\t\t\t: {0:d}'.format(
                sub_record_data_offset))
            print(u'Sub record data size\t\t\t\t\t\t\t: {0:d}'.format(
                sub_record_data_size - sub_record_data_offset))
            if sub_record_data_offset < sub_record_data_size:
                print(u'')

        while sub_record_data_offset < sub_record_data_size:
            read_size = self._ReadRecord(change_log_entry_record_data,
                                         sub_record_data_offset)
            if read_size == 0:
                break
            sub_record_data_offset += read_size

        copy_of_record_size = construct.ULInt32(u'record_size').parse(
            change_log_entry_record_data[-4:])
        if record_size != copy_of_record_size:
            raise IOError(u'Record size mismatch ({0:d} != {1:d})'.format(
                record_size, copy_of_record_size))

        if self._debug:
            print(u'Copy of record size\t\t\t\t\t\t\t: {0:d}'.format(
                copy_of_record_size))
            print(u'')

        return change_log_entry

    def _ReadFileHeader(self):
        """Reads the file header.

    Raises:
      IOError: if the file header cannot be read.
    """
        if self._debug:
            print(u'Seeking file header offset: 0x{0:08x}'.format(0))

        self._file_object.seek(0, os.SEEK_SET)

        try:
            file_header_struct = self._FILE_HEADER.parse_stream(
                self._file_object)
        except construct.FieldError as exception:
            raise IOError(
                u'Unable to parse file header with error: {0:s}'.format(
                    exception))

        signature = file_header_struct.get(u'signature')
        if signature != self.SIGNATURE:
            raise IOError(u'Unsupported change.log file signature')

        record_size = file_header_struct.get(u'record_size')

        record_type = file_header_struct.get(u'record_type')
        if record_type != 0:
            raise IOError(
                u'Unsupported record type: {0:d}'.format(record_type))

        format_version = file_header_struct.get(u'format_version')
        if format_version != 2:
            raise IOError(
                u'Unsupported change.log format version: {0:d}'.format(
                    format_version))

        self._file_object.seek(0, os.SEEK_SET)

        file_header_data = self._file_object.read(record_size)

        if self._debug:
            print(u'File header data:')
            print(hexdump.Hexdump(file_header_data))

        if self._debug:
            print(u'Record size\t\t\t\t\t\t\t\t: {0:d}'.format(record_size))
            print(u'Record type\t\t\t\t\t\t\t\t: {0:d} ({1:s})'.format(
                record_type, self._RECORD_TYPES.get(record_type, u'Unknown')))
            print(u'Signature\t\t\t\t\t\t\t\t: 0x{0:08x}'.format(signature))
            print(u'Format version\t\t\t\t\t\t\t\t: {0:d}'.format(
                format_version))

        self._ReadVolumePath(file_header_data[16:-4])

        copy_of_record_size = construct.ULInt32(u'record_size').parse(
            file_header_data[-4:])
        if record_size != copy_of_record_size:
            raise IOError(u'Record size mismatch ({0:d} != {1:d})'.format(
                record_size, copy_of_record_size))

        if self._debug:
            print(u'Copy of record size\t\t\t\t\t\t\t: {0:d}'.format(
                copy_of_record_size))
            print(u'')

    def _ReadRecord(self, record_data, record_data_offset):
        """Reads a record.

    Args:
      record_data: the record data.
      record_data_offset: the record data offset.

    Returns:
      The record size.

    Raises:
      IOError: if the record cannot be read.
    """
        try:
            record_header_struct = self._RECORD_HEADER.parse(
                record_data[record_data_offset:])
        except construct.FieldError as exception:
            raise IOError(
                u'Unable to parse record header with error: {0:s}'.format(
                    exception))

        record_size = record_header_struct.get(u'record_size')
        record_type = record_header_struct.get(u'record_type')

        if self._debug:
            print(u'Record data:')
            print(
                hexdump.Hexdump(
                    record_data[record_data_offset:record_data_offset +
                                record_size]))

        if self._debug:
            print(u'Record size\t\t\t\t\t\t\t\t: {0:d}'.format(record_size))
            print(u'Record type\t\t\t\t\t\t\t\t: {0:d} ({1:s})'.format(
                record_type, self._RECORD_TYPES.get(record_type, u'Unknown')))

        record_data_offset += self._RECORD_HEADER.sizeof()

        if record_type in [4, 5, 9]:
            try:
                utf16_stream = self._UTF16_STREAM.parse(
                    record_data[record_data_offset:])
            except construct.FieldError as exception:
                raise IOError(
                    u'Unable to parse UTF-16 stream with error: {0:s}'.format(
                        exception))

            try:
                # The UTF-16 stream includes the end-of-string character that we need
                # to strip off.
                value_string = b''.join(utf16_stream).decode(u'utf16')[:-1]
            except UnicodeDecodeError as exception:
                value_string = u''

        # TODO: add support for other record types.
        # TODO: store record values in runtime objects.

        if self._debug:
            if record_type == 4:
                print(u'Secondary path\t\t\t\t\t\t\t\t: {0:s}'.format(
                    value_string))
            elif record_type == 5:
                print(u'Backup filename\t\t\t\t\t\t\t\t: {0:s}'.format(
                    value_string))
            elif record_type == 9:
                print(u'Short filename\t\t\t\t\t\t\t\t: {0:s}'.format(
                    value_string))

            print(u'')

        return record_size

    def _ReadVolumePath(self, volume_path_record_data):
        """Reads the volume path.

    Args:
      volume_path_record_data: the volume path record data.

    Raises:
      IOError: if the volume path cannot be read.
    """
        try:
            volume_path_struct = self._VOLUME_PATH.parse(
                volume_path_record_data)
        except construct.FieldError as exception:
            raise IOError(
                u'Unable to parse volume path with error: {0:s}'.format(
                    exception))

        record_size = volume_path_struct.get(u'record_size')

        record_type = volume_path_struct.get(u'record_type')
        if record_type != 2:
            raise IOError(
                u'Unsupported record type: {0:d}'.format(record_type))

        if self._debug:
            print(u'Volume path record data:')
            print(hexdump.Hexdump(volume_path_record_data))

        try:
            # The struct includes the end-of-string character that we need
            # to strip off.
            self.volume_path = volume_path_struct.get(u'volume_path')
            self.volume_path = b''.join(self.volume_path).decode(u'utf16')[:-1]
        except UnicodeDecodeError as exception:
            self.volume_path = u''

        if self._debug:
            print(u'Record size\t\t\t\t\t\t\t\t: {0:d}'.format(record_size))
            print(u'Record type\t\t\t\t\t\t\t\t: {0:d} ({1:s})'.format(
                record_type, self._RECORD_TYPES.get(record_type, u'Unknown')))
            print(u'Volume path\t\t\t\t\t\t\t\t: {0:s}'.format(
                self.volume_path))
            print(u'')

    def Close(self):
        """Closes the change.log file."""
        if self._file_object_opened_in_object:
            self._file_object.close()
        self._file_object = None

    def Open(self, filename):
        """Opens the change.log file.

    Args:
      filename: the filename.
    """
        stat_object = os.stat(filename)
        self._file_size = stat_object.st_size

        self._file_object = open(filename, 'rb')
        self._file_object_opened_in_object = True
        self._ReadFileHeader()
        self._ReadChangeLogEntries()
예제 #8
0
파일: wemf.py 프로젝트: kalnyc1/assorted
class WMFFile(object):
    """Class that contains a WMF (.wmf) file."""

    _WMF_FILE_HEADER = construct.Struct(
        u'wmf_file_header', construct.ULInt16(u'file_type'),
        construct.ULInt16(u'record_size'),
        construct.ULInt16(u'format_version'), construct.ULInt32(u'file_size'),
        construct.ULInt16(u'maximum_number_of_objects'),
        construct.ULInt32(u'largest_record_size'),
        construct.ULInt16(u'number_of_records'))

    _WMF_RECORD_HEADER = construct.Struct(u'wmf_record_header',
                                          construct.ULInt32(u'record_size'),
                                          construct.ULInt16(u'record_type'))

    _WMF_RECORD_TYPES = {
        0x0000: u'META_EOF',
        0x001e: u'META_SAVEDC',
        0x0035: u'META_REALIZEPALETTE',
        0x0037: u'META_SETPALENTRIES',
        0x00f7: u'META_CREATEPALETTE',
        0x0102: u'META_SETBKMODE',
        0x0103: u'META_SETMAPMODE',
        0x0104: u'META_SETROP2',
        0x0105: u'META_SETRELABS',
        0x0106: u'META_SETPOLYFILLMODE',
        0x0107: u'META_SETSTRETCHBLTMODE',
        0x0108: u'META_SETTEXTCHAREXTRA',
        0x0127: u'META_RESTOREDC',
        0x012a: u'META_INVERTREGION',
        0x012b: u'META_PAINTREGION',
        0x012c: u'META_SELECTCLIPREGION',
        0x012d: u'META_SELECTOBJECT',
        0x012e: u'META_SETTEXTALIGN',
        0x0139: u'META_RESIZEPALETTE',
        0x0142: u'META_DIBCREATEPATTERNBRUSH',
        0x0149: u'META_SETLAYOUT',
        0x01f0: u'META_DELETEOBJECT',
        0x01f9: u'META_CREATEPATTERNBRUSH',
        0x0201: u'META_SETBKCOLOR',
        0x0209: u'META_SETTEXTCOLOR',
        0x020a: u'META_SETTEXTJUSTIFICATION',
        0x020b: u'META_SETWINDOWORG',
        0x020c: u'META_SETWINDOWEXT',
        0x020d: u'META_SETVIEWPORTORG',
        0x020e: u'META_SETVIEWPORTEXT',
        0x020f: u'META_OFFSETWINDOWORG',
        0x0211: u'META_OFFSETVIEWPORTORG',
        0x0213: u'META_LINETO',
        0x0214: u'META_MOVETO',
        0x0220: u'META_OFFSETCLIPRGN',
        0x0228: u'META_FILLREGION',
        0x0231: u'META_SETMAPPERFLAGS',
        0x0234: u'META_SELECTPALETTE',
        0x02fa: u'META_CREATEPENINDIRECT',
        0x02fb: u'META_CREATEFONTINDIRECT',
        0x02fc: u'META_CREATEBRUSHINDIRECT',
        0x0324: u'META_POLYGON',
        0x0325: u'META_POLYLINE',
        0x0410: u'META_SCALEWINDOWEXT',
        0x0412: u'META_SCALEVIEWPORTEXT',
        0x0415: u'META_EXCLUDECLIPRECT',
        0x0416: u'META_INTERSECTCLIPRECT',
        0x0418: u'META_ELLIPSE',
        0x0419: u'META_FLOODFILL',
        0x041B: u'META_RECTANGLE',
        0x041F: u'META_SETPIXEL',
        0x0429: u'META_FRAMEREGION',
        0x0436: u'META_ANIMATEPALETTE',
        0x0521: u'META_TEXTOUT',
        0x0538: u'META_POLYPOLYGON',
        0x0548: u'META_EXTFLOODFILL',
        0x061C: u'META_ROUNDRECT',
        0x061d: u'META_PATBLT',
        0x0626: u'META_ESCAPE',
        0x06ff: u'META_CREATEREGION',
        0x0817: u'META_ARC',
        0x081a: u'META_PIE',
        0x0830: u'META_CHORD',
        0x0922: u'META_BITBLT',
        0x0940: u'META_DIBBITBLT',
        0x0a32: u'META_EXTTEXTOUT',
        0x0b23: u'META_STRETCHBLT',
        0x0b41: u'META_DIBSTRETCHBLT',
        0x0d33: u'META_SETDIBTODEV',
        0x0f43: u'META_STRETCHDIB'
    }

    _WMF_SETMAPMODE = construct.Struct(u'wmf_setmapmode',
                                       construct.ULInt16(u'map_mode'))

    _WMF_SETSTRETCHBLTMODE = construct.Struct(
        u'wmf_setstretchbltmode', construct.ULInt16(u'stretch_mode'))
    # TODO: documentation indicates there should be 16-bit reserved field.

    _WMF_RESTOREDC = construct.Struct(
        u'wmf_restoredc', construct.ULInt16(u'number_of_saved_device_context'))

    _WMF_SETWINDOWORG = construct.Struct(u'wmf_setwindoworg',
                                         construct.ULInt16(u'x_coordinate'),
                                         construct.ULInt16(u'y_coordinate'))

    _WMF_SETWINDOWEXT = construct.Struct(u'wmf_setwindowext',
                                         construct.ULInt16(u'x_coordinate'),
                                         construct.ULInt16(u'y_coordinate'))

    _WMF_DIBSTRETCHBLT = construct.Struct(
        u'wmf_dibstretchblt', construct.ULInt32(u'raster_operation'),
        construct.ULInt16(u'source_height'),
        construct.ULInt16(u'source_width'),
        construct.ULInt16(u'source_x_coordinate'),
        construct.ULInt16(u'source_y_coordinate'),
        construct.ULInt16(u'destination_height'),
        construct.ULInt16(u'destination_width'),
        construct.ULInt16(u'destination_x_coordinate'),
        construct.ULInt16(u'destination_y_coordinate'),
        construct.Anchor(u'device_indepent_bitmap'))

    # record_size == ((record_type >> 8) + 3)
    # DIB: https://msdn.microsoft.com/en-us/library/cc250593.aspx

    # Here None represents that the record has no additional data.
    _WMF_RECORD_DATA_STRUCT_TYPES = {
        0x0000: None,
        0x001e: None,
        0x0103: _WMF_SETMAPMODE,
        0x0107: _WMF_SETSTRETCHBLTMODE,
        0x0127: _WMF_RESTOREDC,
        0x020b: _WMF_SETWINDOWORG,
        0x020c: _WMF_SETWINDOWEXT,
        0x0b41: _WMF_DIBSTRETCHBLT
    }

    _WMF_MAP_MODES = {
        0x0001: u'MM_TEXT',
        0x0002: u'MM_LOMETRIC',
        0x0003: u'MM_HIMETRIC',
        0x0004: u'MM_LOENGLISH',
        0x0005: u'MM_HIENGLISH',
        0x0006: u'MM_TWIPS',
        0x0007: u'MM_ISOTROPIC',
        0x0008: u'MM_ANISOTROPIC'
    }

    _WMF_RASTER_OPERATION_CODES = {
        0x00: u'BLACKNESS',
        0x01: u'DPSOON',
        0x02: u'DPSONA',
        0x03: u'PSON',
        0x04: u'SDPONA',
        0x05: u'DPON',
        0x06: u'PDSXNON',
        0x07: u'PDSAON',
        0x08: u'SDPNAA',
        0x09: u'PDSXON',
        0x0a: u'DPNA',
        0x0b: u'PSDNAON',
        0x0c: u'SPNA',
        0x0d: u'PDSNAON',
        0x0e: u'PDSONON',
        0x0f: u'PN',
        0x10: u'PDSONA',
        0x11: u'NOTSRCERASE',
        0x12: u'SDPXNON',
        0x13: u'SDPAON',
        0x14: u'DPSXNON',
        0x15: u'DPSAON',
        0x16: u'PSDPSANAXX',
        0x17: u'SSPXDSXAXN',
        0x18: u'SPXPDXA',
        0x19: u'SDPSANAXN',
        0x1a: u'PDSPAOX',
        0x1b: u'SDPSXAXN',
        0x1c: u'PSDPAOX',
        0x1d: u'DSPDXAXN',
        0x1e: u'PDSOX',
        0x1f: u'PDSOAN',
        0x20: u'DPSNAA',
        0x21: u'SDPXON',
        0x22: u'DSNA',
        0x23: u'SPDNAON',
        0x24: u'SPXDSXA',
        0x25: u'PDSPANAXN',
        0x26: u'SDPSAOX',
        0x27: u'SDPSXNOX',
        0x28: u'DPSXA',
        0x29: u'PSDPSAOXXN',
        0x2a: u'DPSANA',
        0x2b: u'SSPXPDXAXN',
        0x2c: u'SPDSOAX',
        0x2d: u'PSDNOX',
        0x2e: u'PSDPXOX',
        0x2f: u'PSDNOAN',
        0x30: u'PSNA',
        0x31: u'SDPNAON',
        0x32: u'SDPSOOX',
        0x33: u'NOTSRCCOPY',
        0x34: u'SPDSAOX',
        0x35: u'SPDSXNOX',
        0x36: u'SDPOX',
        0x37: u'SDPOAN',
        0x38: u'PSDPOAX',
        0x39: u'SPDNOX',
        0x3a: u'SPDSXOX',
        0x3b: u'SPDNOAN',
        0x3c: u'PSX',
        0x3d: u'SPDSONOX',
        0x3e: u'SPDSNAOX',
        0x3f: u'PSAN',
        0x40: u'PSDNAA',
        0x41: u'DPSXON',
        0x42: u'SDXPDXA',
        0x43: u'SPDSANAXN',
        0x44: u'SRCERASE',
        0x45: u'DPSNAON',
        0x46: u'DSPDAOX',
        0x47: u'PSDPXAXN',
        0x48: u'SDPXA',
        0x49: u'PDSPDAOXXN',
        0x4a: u'DPSDOAX',
        0x4b: u'PDSNOX',
        0x4c: u'SDPANA',
        0x4d: u'SSPXDSXOXN',
        0x4e: u'PDSPXOX',
        0x4f: u'PDSNOAN',
        0x50: u'PDNA',
        0x51: u'DSPNAON',
        0x52: u'DPSDAOX',
        0x53: u'SPDSXAXN',
        0x54: u'DPSONON',
        0x55: u'DSTINVERT',
        0x56: u'DPSOX',
        0x57: u'DPSOAN',
        0x58: u'PDSPOAX',
        0x59: u'DPSNOX',
        0x5a: u'PATINVERT',
        0x5b: u'DPSDONOX',
        0x5c: u'DPSDXOX',
        0x5d: u'DPSNOAN',
        0x5e: u'DPSDNAOX',
        0x5f: u'DPAN',
        0x60: u'PDSXA',
        0x61: u'DSPDSAOXXN',
        0x62: u'DSPDOAX',
        0x63: u'SDPNOX',
        0x64: u'SDPSOAX',
        0x65: u'DSPNOX',
        0x66: u'SRCINVERT',
        0x67: u'SDPSONOX',
        0x68: u'DSPDSONOXXN',
        0x69: u'PDSXXN',
        0x6a: u'DPSAX',
        0x6b: u'PSDPSOAXXN',
        0x6c: u'SDPAX',
        0x6d: u'PDSPDOAXXN',
        0x6e: u'SDPSNOAX',
        0x6f: u'PDXNAN',
        0x70: u'PDSANA',
        0x71: u'SSDXPDXAXN',
        0x72: u'SDPSXOX',
        0x73: u'SDPNOAN',
        0x74: u'DSPDXOX',
        0x75: u'DSPNOAN',
        0x76: u'SDPSNAOX',
        0x77: u'DSAN',
        0x78: u'PDSAX',
        0x79: u'DSPDSOAXXN',
        0x7a: u'DPSDNOAX',
        0x7b: u'SDPXNAN',
        0x7c: u'SPDSNOAX',
        0x7d: u'DPSXNAN',
        0x7e: u'SPXDSXO',
        0x7f: u'DPSAAN',
        0x80: u'DPSAA',
        0x81: u'SPXDSXON',
        0x82: u'DPSXNA',
        0x83: u'SPDSNOAXN',
        0x84: u'SDPXNA',
        0x85: u'PDSPNOAXN',
        0x86: u'DSPDSOAXX',
        0x87: u'PDSAXN',
        0x88: u'SRCAND',
        0x89: u'SDPSNAOXN',
        0x8a: u'DSPNOA',
        0x8b: u'DSPDXOXN',
        0x8c: u'SDPNOA',
        0x8d: u'SDPSXOXN',
        0x8e: u'SSDXPDXAX',
        0x8f: u'PDSANAN',
        0x90: u'PDSXNA',
        0x91: u'SDPSNOAXN',
        0x92: u'DPSDPOAXX',
        0x93: u'SPDAXN',
        0x94: u'PSDPSOAXX',
        0x95: u'DPSAXN',
        0x96: u'DPSXX',
        0x97: u'PSDPSONOXX',
        0x98: u'SDPSONOXN',
        0x99: u'DSXN',
        0x9a: u'DPSNAX',
        0x9b: u'SDPSOAXN',
        0x9c: u'SPDNAX',
        0x9d: u'DSPDOAXN',
        0x9e: u'DSPDSAOXX',
        0x9f: u'PDSXAN',
        0xa0: u'DPA',
        0xa1: u'PDSPNAOXN',
        0xa2: u'DPSNOA',
        0xa3: u'DPSDXOXN',
        0xa4: u'PDSPONOXN',
        0xa5: u'PDXN',
        0xa6: u'DSPNAX',
        0xa7: u'PDSPOAXN',
        0xa8: u'DPSOA',
        0xa9: u'DPSOXN',
        0xaa: u'D',
        0xab: u'DPSONO',
        0xac: u'SPDSXAX',
        0xad: u'DPSDAOXN',
        0xae: u'DSPNAO',
        0xaf: u'DPNO',
        0xb0: u'PDSNOA',
        0xb1: u'PDSPXOXN',
        0xb2: u'SSPXDSXOX',
        0xb3: u'SDPANAN',
        0xb4: u'PSDNAX',
        0xb5: u'DPSDOAXN',
        0xb6: u'DPSDPAOXX',
        0xb7: u'SDPXAN',
        0xb8: u'PSDPXAX',
        0xb9: u'DSPDAOXN',
        0xba: u'DPSNAO',
        0xbb: u'MERGEPAINT',
        0xbc: u'SPDSANAX',
        0xbd: u'SDXPDXAN',
        0xbe: u'DPSXO',
        0xbf: u'DPSANO',
        0xc0: u'MERGECOPY',
        0xc1: u'SPDSNAOXN',
        0xc2: u'SPDSONOXN',
        0xc3: u'PSXN',
        0xc4: u'SPDNOA',
        0xc5: u'SPDSXOXN',
        0xc6: u'SDPNAX',
        0xc7: u'PSDPOAXN',
        0xc8: u'SDPOA',
        0xc9: u'SPDOXN',
        0xca: u'DPSDXAX',
        0xcb: u'SPDSAOXN',
        0xcc: u'SRCCOPY',
        0xcd: u'SDPONO',
        0xce: u'SDPNAO',
        0xcf: u'SPNO',
        0xd0: u'PSDNOA',
        0xd1: u'PSDPXOXN',
        0xd2: u'PDSNAX',
        0xd3: u'SPDSOAXN',
        0xd4: u'SSPXPDXAX',
        0xd5: u'DPSANAN',
        0xd6: u'PSDPSAOXX',
        0xd7: u'DPSXAN',
        0xd8: u'PDSPXAX',
        0xd9: u'SDPSAOXN',
        0xda: u'DPSDANAX',
        0xdb: u'SPXDSXAN',
        0xdc: u'SPDNAO',
        0xdd: u'SDNO',
        0xde: u'SDPXO',
        0xdf: u'SDPANO',
        0xe0: u'PDSOA',
        0xe1: u'PDSOXN',
        0xe2: u'DSPDXAX',
        0xe3: u'PSDPAOXN',
        0xe4: u'SDPSXAX',
        0xe5: u'PDSPAOXN',
        0xe6: u'SDPSANAX',
        0xe7: u'SPXPDXAN',
        0xe8: u'SSPXDSXAX',
        0xe9: u'DSPDSANAXXN',
        0xea: u'DPSAO',
        0xeb: u'DPSXNO',
        0xec: u'SDPAO',
        0xed: u'SDPXNO',
        0xee: u'SRCPAINT',
        0xef: u'SDPNOO',
        0xf0: u'PATCOPY',
        0xf1: u'PDSONO',
        0xf2: u'PDSNAO',
        0xf3: u'PSNO',
        0xf4: u'PSDNAO',
        0xf5: u'PDNO',
        0xf6: u'PDSXO',
        0xf7: u'PDSANO',
        0xf8: u'PDSAO',
        0xf9: u'PDSXNO',
        0xfa: u'DPO',
        0xfb: u'PATPAINT',
        0xfc: u'PSO',
        0xfd: u'PSDNOO',
        0xfe: u'DPSOO',
        0xff: u'WHITENESS'
    }

    # https://msdn.microsoft.com/en-us/library/windows/desktop/
    # dd183370(v=vs.85).aspx
    _WMF_RASTER_OPERATIONS = {
        0x00000042: u'BLACKNES',
        0x00010289: u'DPSOO',
        0x00020C89: u'DPSON',
        0x000300AA: u'PSO',
        0x00040C88: u'SDPON',
        0x000500A9: u'DPO',
        0x00060865: u'PDSXNO',
        0x000702C5: u'PDSAO',
        0x00080F08: u'SDPNA',
        0x00090245: u'PDSXO',
        0x000A0329: u'DPN',
        0x000B0B2A: u'PSDNAO',
        0x000C0324: u'SPN',
        0x000D0B25: u'PDSNAO',
        0x000E08A5: u'PDSONO',
        0x000F0001: u'P',
        0x00100C85: u'PDSON',
        0x001100A6: u'NOTSRCERAS',
        0x00120868: u'SDPXNO',
        0x001302C8: u'SDPAO',
        0x00140869: u'DPSXNO',
        0x001502C9: u'DPSAO',
        0x00165CCA: u'PSDPSANAX',
        0x00171D54: u'SSPXDSXAX',
        0x00180D59: u'SPXPDX',
        0x00191CC8: u'SDPSANAX',
        0x001A06C5: u'PDSPAO',
        0x001B0768: u'SDPSXAX',
        0x001C06CA: u'PSDPAO',
        0x001D0766: u'DSPDXAX',
        0x001E01A5: u'PDSO',
        0x001F0385: u'PDSOA',
        0x00200F09: u'DPSNA',
        0x00210248: u'SDPXO',
        0x00220326: u'DSN',
        0x00230B24: u'SPDNAO',
        0x00240D55: u'SPXDSX',
        0x00251CC5: u'PDSPANAX',
        0x002606C8: u'SDPSAO',
        0x00271868: u'SDPSXNOX',
        0x00280369: u'DPSXA',
        0x002916CA: u'PSDPSAOXXN',
        0x002A0CC9: u'DPSANA',
        0x002B1D58: u'SSPXPDXAXN',
        0x002C0784: u'SPDSOAX',
        0x002D060A: u'PSDNOX',
        0x002E064A: u'PSDPXOX',
        0x002F0E2A: u'PSDNOAN',
        0x0030032A: u'PSNA',
        0x00310B28: u'SDPNAON',
        0x00320688: u'SDPSOOX',
        0x00330008: u'NOTSRCCOPY',
        0x003406C4: u'SPDSAOX',
        0x00351864: u'SPDSXNOX',
        0x003601A8: u'SDPOX',
        0x00370388: u'SDPOAN',
        0x0038078A: u'PSDPOAX',
        0x00390604: u'SPDNOX',
        0x003A0644: u'SPDSXOX',
        0x003B0E24: u'SPDNOAN',
        0x003C004A: u'PSX',
        0x003D18A4: u'SPDSONOX',
        0x003E1B24: u'SPDSNAOX',
        0x003F00EA: u'PSAN',
        0x00400F0A: u'PSDNAA',
        0x00410249: u'DPSXON',
        0x00420D5D: u'SDXPDXA',
        0x00431CC4: u'SPDSANAXN',
        0x00440328: u'SRCERASE',
        0x00450B29: u'DPSNAON',
        0x004606C6: u'DSPDAOX',
        0x0047076A: u'PSDPXAXN',
        0x00480368: u'SDPXA',
        0x004916C5: u'PDSPDAOXXN',
        0x004A0789: u'DPSDOAX',
        0x004B0605: u'PDSNOX',
        0x004C0CC8: u'SDPANA',
        0x004D1954: u'SSPXDSXOXN',
        0x004E0645: u'PDSPXOX',
        0x004F0E25: u'PDSNOAN',
        0x00500325: u'PDNA',
        0x00510B26: u'DSPNAON',
        0x005206C9: u'DPSDAOX',
        0x00530764: u'SPDSXAXN',
        0x005408A9: u'DPSONON',
        0x00550009: u'DSTINVERT',
        0x005601A9: u'DPSOX',
        0x000570389: u'DPSOAN',
        0x00580785: u'PDSPOAX',
        0x00590609: u'DPSNOX',
        0x005A0049: u'PATINVERT',
        0x005B18A9: u'DPSDONOX',
        0x005C0649: u'DPSDXOX',
        0x005D0E29: u'DPSNOAN',
        0x005E1B29: u'DPSDNAOX',
        0x005F00E9: u'DPAN',
        0x00600365: u'PDSXA',
        0x006116C6: u'DSPDSAOXXN',
        0x00620786: u'DSPDOAX',
        0x00630608: u'SDPNOX',
        0x00640788: u'SDPSOAX',
        0x00650606: u'DSPNOX',
        0x00660046: u'SRCINVERT',
        0x006718A8: u'SDPSONOX',
        0x006858A6: u'DSPDSONOXXN',
        0x00690145: u'PDSXXN',
        0x006A01E9: u'DPSAX',
        0x006B178A: u'PSDPSOAXXN',
        0x006C01E8: u'SDPAX',
        0x006D1785: u'PDSPDOAXXN',
        0x006E1E28: u'SDPSNOAX',
        0x006F0C65: u'PDXNAN',
        0x00700CC5: u'PDSANA',
        0x00711D5C: u'SSDXPDXAXN',
        0x00720648: u'SDPSXOX',
        0x00730E28: u'SDPNOAN',
        0x00740646: u'DSPDXOX',
        0x00750E26: u'DSPNOAN',
        0x00761B28: u'SDPSNAOX',
        0x007700E6: u'DSAN',
        0x007801E5: u'PDSAX',
        0x00791786: u'DSPDSOAXXN',
        0x007A1E29: u'DPSDNOAX',
        0x007B0C68: u'SDPXNAN',
        0x007C1E24: u'SPDSNOAX',
        0x007D0C69: u'DPSXNAN',
        0x007E0955: u'SPXDSXO',
        0x007F03C9: u'DPSAAN',
        0x008003E9: u'DPSAA',
        0x00810975: u'SPXDSXON',
        0x00820C49: u'DPSXNA',
        0x00831E04: u'SPDSNOAXN',
        0x00840C48: u'SDPXNA',
        0x00851E05: u'PDSPNOAXN',
        0x008617A6: u'DSPDSOAXX',
        0x008701C5: u'PDSAXN',
        0x008800C6: u'SRCAND',
        0x00891B08: u'SDPSNAOXN',
        0x008A0E06: u'DSPNOA',
        0x008B0666: u'DSPDXOXN',
        0x008C0E08: u'SDPNOA',
        0x008D0668: u'SDPSXOXN',
        0x008E1D7C: u'SSDXPDXAX',
        0x008F0CE5: u'PDSANAN',
        0x00900C45: u'PDSXNA',
        0x00911E08: u'SDPSNOAXN',
        0x009217A9: u'DPSDPOAXX',
        0x009301C4: u'SPDAXN',
        0x009417AA: u'PSDPSOAXX',
        0x009501C9: u'DPSAXN',
        0x00960169: u'DPSXX',
        0x0097588A: u'PSDPSONOXX',
        0x00981888: u'SDPSONOXN',
        0x00990066: u'DSXN',
        0x009A0709: u'DPSNAX',
        0x009B07A8: u'SDPSOAXN',
        0x009C0704: u'SPDNAX',
        0x009D07A6: u'DSPDOAXN',
        0x009E16E6: u'DSPDSAOXX',
        0x009F0345: u'PDSXAN',
        0x00A000C9: u'DPA',
        0x00A11B05: u'PDSPNAOXN',
        0x00A20E09: u'DPSNOA',
        0x00A30669: u'DPSDXOXN',
        0x00A41885: u'PDSPONOXN',
        0x00A50065: u'PDXN',
        0x00A60706: u'DSPNAX',
        0x00A707A5: u'PDSPOAXN',
        0x00A803A9: u'DPSOA',
        0x00A90189: u'DPSOXN',
        0x00AA0029: u'D',
        0x00AB0889: u'DPSONO',
        0x00AC0744: u'SPDSXAX',
        0x00AD06E9: u'DPSDAOXN',
        0x00AE0B06: u'DSPNAO',
        0x00AF0229: u'DPNO',
        0x00B00E05: u'PDSNOA',
        0x00B10665: u'PDSPXOXN',
        0x00B21974: u'SSPXDSXOX',
        0x00B30CE8: u'SDPANAN',
        0x00B4070A: u'PSDNAX',
        0x00B507A9: u'DPSDOAXN',
        0x00B616E9: u'DPSDPAOXX',
        0x00B70348: u'SDPXAN',
        0x00B8074A: u'PSDPXAX',
        0x00B906E6: u'DSPDAOXN',
        0x00BA0B09: u'DPSNAO',
        0x00BB0226: u'MERGEPAINT',
        0x00BC1CE4: u'SPDSANAX',
        0x00BD0D7D: u'SDXPDXAN',
        0x00BE0269: u'DPSXO',
        0x00BF08C9: u'DPSANO',
        0x00C000CA: u'MERGECOPY',
        0x00C11B04: u'SPDSNAOXN',
        0x00C21884: u'SPDSONOXN',
        0x00C3006A: u'PSXN',
        0x00C40E04: u'SPDNOA',
        0x00C50664: u'SPDSXOXN',
        0x00C60708: u'SDPNAX',
        0x00C707AA: u'PSDPOAXN',
        0x00C803A8: u'SDPOA',
        0x00C90184: u'SPDOXN',
        0x00CA0749: u'DPSDXAX',
        0x00CB06E4: u'SPDSAOXN',
        0x00CC0020: u'SRCCOPY',
        0x00CD0888: u'SDPONO',
        0x00CE0B08: u'SDPNAO',
        0x00CF0224: u'SPNO',
        0x00D00E0A: u'PSDNOA',
        0x00D1066A: u'PSDPXOXN',
        0x00D20705: u'PDSNAX',
        0x00D307A4: u'SPDSOAXN',
        0x00D41D78: u'SSPXPDXAX',
        0x00D50CE9: u'DPSANAN',
        0x00D616EA: u'PSDPSAOXX',
        0x00D70349: u'DPSXAN',
        0x00D80745: u'PDSPXAX',
        0x00D906E8: u'SDPSAOXN',
        0x00DA1CE9: u'DPSDANAX',
        0x00DB0D75: u'SPXDSXAN',
        0x00DC0B04: u'SPDNAO',
        0x00DD0228: u'SDNO',
        0x00DE0268: u'SDPXO',
        0x00DF08C8: u'SDPANO',
        0x00E003A5: u'PDSOA',
        0x00E10185: u'PDSOXN',
        0x00E20746: u'DSPDXAX',
        0x00E306EA: u'PSDPAOXN',
        0x00E40748: u'SDPSXAX',
        0x00E506E5: u'PDSPAOXN',
        0x00E61CE8: u'SDPSANAX',
        0x00E70D79: u'SPXPDXAN',
        0x00E81D74: u'SSPXDSXAX',
        0x00E95CE6: u'DSPDSANAXXN',
        0x00EA02E9: u'DPSAO',
        0x00EB0849: u'DPSXNO',
        0x00EC02E8: u'SDPAO',
        0x00ED0848: u'SDPXNO',
        0x00EE0086: u'SRCPAINT',
        0x00EF0A08: u'SDPNOO',
        0x00F00021: u'PATCOPY',
        0x00F10885: u'PDSONO',
        0x00F20B05: u'PDSNAO',
        0x00F3022A: u'PSNO',
        0x00F40B0A: u'PSDNAO',
        0x00F50225: u'PDNO',
        0x00F60265: u'PDSXO',
        0x00F708C5: u'PDSANO',
        0x00F802E5: u'PDSAO',
        0x00F90845: u'PDSXNO',
        0x00FA0089: u'DPO',
        0x00FB0A09: u'PATPAINT',
        0x00FC008A: u'PSO',
        0x00FD0A0A: u'PSDNOO',
        0x00FE02A9: u'DPSOO',
        0x00FF0062: u'WHITENESS'
    }

    _WMF_STRETCH_MODES = {
        0x0001: u'BLACKONWHITE',
        0x0002: u'WHITEONBLACK',
        0x0003: u'COLORONCOLOR',
        0x0004: u'HALFTONE'
    }

    def __init__(self, debug=False):
        """Initializes an WMF file.

    Args:
      debug (Optional[bool]): True if debug information should be printed.
    """
        super(WMFFile, self).__init__()
        self._debug = debug
        self._file_object = None
        self._file_object_opened_in_object = False
        self._file_size = 0

    def _ReadFileHeader(self):
        """Reads a file header.

    Raises:
      IOError: if the file header cannot be read.
    """
        if self._debug:
            print(u'Seeking file header offset: 0x{0:08x}'.format(0))

        self._file_object.seek(0, os.SEEK_SET)

        file_header_data = self._file_object.read(
            self._WMF_FILE_HEADER.sizeof())

        if self._debug:
            print(u'File header data:')
            print(hexdump.Hexdump(file_header_data))

        try:
            wmf_file_header_struct = self._WMF_FILE_HEADER.parse(
                file_header_data)
        except construct.FieldError as exception:
            raise IOError((u'Unable to parse file header with error: {0:s}'
                           ).format(exception))

        if self._debug:
            print(u'File type\t\t\t\t\t\t\t: 0x{0:04x}'.format(
                wmf_file_header_struct.file_type))
            print(u'Record size\t\t\t\t\t\t\t: {0:d}'.format(
                wmf_file_header_struct.record_size))

            print(u'Format version\t\t\t\t\t\t\t: {0:d}'.format(
                wmf_file_header_struct.format_version))
            print(u'File size\t\t\t\t\t\t\t: {0:d}'.format(
                wmf_file_header_struct.file_size))
            print(u'Maximum number of object\t\t\t\t\t: {0:d}'.format(
                wmf_file_header_struct.maximum_number_of_objects))
            print(u'Largest record size\t\t\t\t\t\t: {0:d}'.format(
                wmf_file_header_struct.largest_record_size))
            print(u'Number of records\t\t\t\t\t\t: {0:d}'.format(
                wmf_file_header_struct.number_of_records))

            print(u'')

        if wmf_file_header_struct.file_type not in (1, 2):
            raise IOError(u'Unsupported file type: {0:d}'.format(
                wmf_file_header_struct.file_type))

        if wmf_file_header_struct.record_size != 9:
            raise IOError(u'Unsupported record size: {0:d}'.format(
                wmf_file_header_struct.record_size))

    def _ReadRecord(self, file_offset):
        """Reads a record.

    Args:
      file_offset: an integer containing the file offset of the record.

    Raises:
      IOError: if the record cannot be read.
    """
        if self._debug:
            print(u'Seeking record offset: 0x{0:08x}'.format(file_offset))

        self._file_object.seek(file_offset, os.SEEK_SET)

        record_header_data_size = self._WMF_RECORD_HEADER.sizeof()
        record_header_data = self._file_object.read(record_header_data_size)

        if self._debug:
            print(u'Record header data:')
            print(hexdump.Hexdump(record_header_data))

        try:
            wmf_record_header_struct = self._WMF_RECORD_HEADER.parse(
                record_header_data)
        except construct.FieldError as exception:
            raise IOError((u'Unable to parse record header with error: {0:s}'
                           ).format(exception))

        record_size = wmf_record_header_struct.record_size * 2

        if self._debug:
            print(u'Record size\t\t\t\t\t\t\t: {0:d} ({1:d})'.format(
                wmf_record_header_struct.record_size, record_size))

            record_type_string = self._WMF_RECORD_TYPES.get(
                wmf_record_header_struct.record_type, u'UNKNOWN')
            print(u'Record type\t\t\t\t\t\t\t: 0x{0:04x} ({1:s})'.format(
                wmf_record_header_struct.record_type, record_type_string))

            print(u'')

        data_offset = file_offset + record_header_data_size
        data_size = record_size - record_header_data_size

        if self._debug:
            self._ReadRecordData(wmf_record_header_struct.record_type,
                                 data_size)

        return Record(wmf_record_header_struct.record_type, record_size,
                      data_offset, data_size)

    def _ReadRecordData(self, record_type, data_size):
        """Reads a record.

    Args:
      record_type (int): record type.
      data_size (int): size of the record data.

    Raises:
      IOError: if the record cannot be read.
    """
        record_data = self._file_object.read(data_size)

        if self._debug and data_size > 0:
            print(u'Record data:')
            print(hexdump.Hexdump(record_data))

        # TODO: use lookup dict with callback.
        struct_type = self._WMF_RECORD_DATA_STRUCT_TYPES.get(record_type, None)
        if not struct_type:
            return

        try:
            record_data_struct = struct_type.parse(record_data)
        except construct.FieldError as exception:
            raise IOError((u'Unable to parse record data with error: {0:s}'
                           ).format(exception))

        if self._debug:
            if record_type == 0x0103:
                map_mode_string = self._WMF_MAP_MODES.get(
                    record_data_struct.map_mode, u'UNKNOWN')
                print(u'Map mode\t\t\t\t\t\t\t: 0x{0:04x} ({1:s})'.format(
                    record_data_struct.map_mode, map_mode_string))

            elif record_type == 0x0107:
                stretch_mode_string = self._WMF_STRETCH_MODES.get(
                    record_data_struct.stretch_mode, u'UNKNOWN')
                print(u'Stretch mode\t\t\t\t\t\t\t: 0x{0:04x} ({1:s})'.format(
                    record_data_struct.stretch_mode, stretch_mode_string))

            elif record_type == 0x0127:
                print(
                    u'Number of saved device context\t\t\t\t\t: {0:d}'.format(
                        record_data_struct.number_of_saved_device_context))

            elif record_type in (0x020b, 0x020c):
                print(u'X coordinate\t\t\t\t\t\t\t: {0:d}'.format(
                    record_data_struct.x_coordinate))
                print(u'Y coordinate\t\t\t\t\t\t\t: {0:d}'.format(
                    record_data_struct.y_coordinate))

            elif record_type == 0x0b41:
                raster_operation_string = self._WMF_RASTER_OPERATIONS.get(
                    record_data_struct.raster_operation, u'UNKNOWN')
                print(
                    u'Raster operation\t\t\t\t\t\t: 0x{0:08x} ({1:s})'.format(
                        record_data_struct.raster_operation,
                        raster_operation_string))

                print(u'Source height\t\t\t\t\t\t\t: {0:d}'.format(
                    record_data_struct.source_height))
                print(u'Source width\t\t\t\t\t\t\t: {0:d}'.format(
                    record_data_struct.source_width))
                print(u'Source X coordinate\t\t\t\t\t\t: {0:d}'.format(
                    record_data_struct.source_x_coordinate))
                print(u'Source Y coordinate\t\t\t\t\t\t: {0:d}'.format(
                    record_data_struct.source_y_coordinate))

                print(u'Destination height\t\t\t\t\t\t: {0:d}'.format(
                    record_data_struct.destination_height))
                print(u'Destination width\t\t\t\t\t\t: {0:d}'.format(
                    record_data_struct.destination_width))
                print(u'Destination X coordinate\t\t\t\t\t: {0:d}'.format(
                    record_data_struct.destination_x_coordinate))
                print(u'Destination Y coordinate\t\t\t\t\t: {0:d}'.format(
                    record_data_struct.destination_y_coordinate))

            print(u'')

    def Close(self):
        """Closes an WMF file."""
        if self._file_object_opened_in_object:
            self._file_object.close()
        self._file_object = None

    def Open(self, filename):
        """Opens an WMF file.

    Args:
      filename (str): filename.
    """
        stat_object = os.stat(filename)
        self._file_size = stat_object.st_size

        self._file_object = open(filename, 'rb')
        self._file_object_opened_in_object = True

        self._ReadFileHeader()

        file_offset = self._file_object.tell()
        while file_offset < self._file_size:
            record = self._ReadRecord(file_offset)

            file_offset += record.size
예제 #9
0
        )
    ),
    construct.ULInt32('size'),
    construct.If(
        lambda ctx: ctx.size > 0,
        construct.Embed(VAULT_ATTRIBUTE_ENCRYPTED)
    ),
    construct.Anchor('attr_ex_pos')
)
'''

VAULT_ATTRIBUTE_EX = construct.Struct(
    'VAULT_ATTRIBUTE_EX', construct.ULInt32('id'),
    construct.ULInt32('attr_unknown_1'), construct.ULInt32('attr_unknown_2'),
    construct.ULInt32('attr_unknown_3'), construct.ULInt32('attr_unknown_4'),
    construct.Anchor('attr_stream_pos'),
    construct.If(
        lambda ctx: ctx.attr_stream_pos % 8,
        construct.Bytes('padding', lambda ctx: 8 - ctx.attr_stream_pos % 8)),
    construct.ULInt32('size'),
    construct.If(lambda ctx: ctx.size > 0,
                 construct.Embed(VAULT_ATTRIBUTE_ENCRYPTED)),
    construct.Anchor('attr_ex_pos'))

VAULT_ATTRIBUTE_EXTRA = construct.Struct(
    'VAULT_ATTRIBUTE_EXTRA', construct.ULInt32('id'),
    construct.ULInt32('attr_unknown_1'), construct.ULInt32('attr_unknown_2'),
    construct.ULInt32('size'),
    construct.If(lambda ctx: ctx.size > 0,
                 construct.Embed(VAULT_ATTRIBUTE_ENCRYPTED)))