Ejemplo n.º 1
0
def _get_data_subblocks(name):
    """Return Adapter to parse GIF data sub-blocks."""
    return construct.ExprAdapter(
        construct.Struct(
            name,
            construct.RepeatUntil(
                lambda obj, ctx: obj.block_size == 0x00,
                construct.Struct(
                    'blocks',
                    construct.ULInt8('block_size'),
                    construct.Bytes('data_values', lambda ctx: ctx.block_size),
                ),
            ),
        ),
        # from comment string, build Containers
        encoder=lambda obj, ctx: construct.Container(blocks=[
            construct.Container(
                block_size=len(chunk),
                data_values=chunk,
            ) for chunk in [obj[i:i + 255] for i in xrange(0, len(obj), 255)]
        ] + [construct.Container(block_size=0, data_values='')], ),
        # from Containers, build comment string
        decoder=lambda obj, ctx: ''.join(dsb.data_values
                                         for dsb in obj.blocks),
    )
Ejemplo n.º 2
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)
Ejemplo n.º 3
0
class MRUListExStringPlugin(interface.ValuePlugin, MRUListExPluginMixin):
  """Windows Registry plugin to parse a string MRUListEx."""

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

  REG_TYPE = 'any'
  REG_VALUES = frozenset(['MRUListEx', '0'])

  URLS = [
      u'http://forensicartifacts.com/2011/02/recentdocs/',
      u'https://code.google.com/p/winreg-kb/wiki/MRUKeys']

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

  def _ParseMRUListExEntryValue(
      self, parser_context, key, entry_index, entry_number, text_dict,
      **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.
    """
    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 value.DataIsString():
      value_string = value.data

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

      try:
        value_string = utf16_stream.decode('utf-16-le')
      except UnicodeDecodeError as exception:
        value_string = binary.HexifyBuffer(utf16_stream)
        logging.warning((
            u'[{0:s}] Unable to decode UTF-16 stream: {1:s} in MRUListEx entry '
            u'value: {2:d} in key: {3:s} with error: {4:s}').format(
                self.NAME, value_string, entry_number, key.path, exception))

    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)

  def Process(self, parser_context, key=None, codepage='cp1252', **kwargs):
    """Determine if we can process this Registry key or not.

    Args:
      parser_context: A parser context object (instance of ParserContext).
      key: A Windows Registry key (instance of WinRegKey).
      codepage: Optional extended ASCII string codepage. The default is cp1252.
    """
    # Prevent this plugin triggering on sub paths of non-string MRUListEx
    # values.
    if (u'BagMRU' in key.path or u'Explorer\\StreamMRU' in key.path or
        u'\\Explorer\\ComDlg32\\OpenSavePidlMRU' in key.path):
      return

    super(MRUListExStringPlugin, self).Process(
        parser_context, key=key, codepage=codepage, **kwargs)
Ejemplo n.º 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)
Ejemplo n.º 5
0
class MRUListExStringPlugin(BaseMRUListExPlugin):
    """Windows Registry plugin to parse a string MRUListEx."""

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

    FILTERS = frozenset([MRUListExStringRegistryKeyFilter()])

    URLS = [
        u'http://forensicartifacts.com/2011/02/recentdocs/',
        u'https://github.com/libyal/winreg-kb/wiki/MRU-keys'
    ]

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

    def _ParseMRUListExEntryValue(self, parser_mediator, key, entry_index,
                                  entry_number, **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.

    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 value.DataIsString():
            value_string = value.GetDataAsObject()

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

            try:
                value_string = utf16_stream.decode(u'utf-16-le')
            except UnicodeDecodeError as exception:
                value_string = binary.HexifyBuffer(utf16_stream)
                logging.warning((
                    u'[{0:s}] Unable to decode UTF-16 stream: {1:s} in MRUListEx entry '
                    u'value: {2:d} in key: {3:s} with error: {4:s}').format(
                        self.NAME, value_string, entry_number, key.path,
                        exception))

        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)
Ejemplo n.º 6
0
class RestorePointLogParser(interface.BaseParser):
    """A parser for Windows Restore Point (rp.log) files."""

    NAME = u'rplog'
    DESCRIPTION = u'Parser for Windows Restore Point (rp.log) files.'

    _FILE_HEADER_STRUCT = construct.Struct(
        u'file_header', construct.ULInt32(u'event_type'),
        construct.ULInt32(u'restore_point_type'),
        construct.ULInt64(u'sequence_number'),
        construct.RepeatUntil(lambda obj, ctx: obj == b'\x00\x00',
                              construct.Field(u'description', 2)))

    _FILE_FOOTER_STRUCT = construct.Struct(u'file_footer',
                                           construct.ULInt64(u'creation_time'))

    def _ParseFileHeader(self, file_object):
        """Parses the file header.

    Args:
      file_object: A file-like object to read data from.

    Returns:
      The file header construct object.
    """
        try:
            file_header = self._FILE_HEADER_STRUCT.parse_stream(file_object)
        except (IOError, construct.FieldError) as exception:
            raise errors.UnableToParseFile(
                u'Unable to parse file header with error: {0:s}'.format(
                    exception))

        if not file_header:
            raise errors.UnableToParseFile(u'Unable to read file header')

        return file_header

    def _ParseFileFooter(self, file_object):
        """Parses the file footer.

    Args:
      file_object: A file-like object to read data from.

    Returns:
      The file footer construct object.
    """
        try:
            file_footer = self._FILE_FOOTER_STRUCT.parse_stream(file_object)
        except (IOError, construct.FieldError) as exception:
            raise errors.UnableToParseFile(
                u'Unable to parse file footer with error: {0:s}'.format(
                    exception))

        if not file_footer:
            raise errors.UnableToParseFile(u'Unable to read file footer')

        return file_footer

    def Parse(self, parser_mediator, **kwargs):
        """Parses a single file.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
    """
        # TODO: for now cheat with detecting this file type.
        file_entry = parser_mediator.GetFileEntry()
        if file_entry.name.lower() != u'rp.log':
            raise errors.UnableToParseFile(u'File not named: rp.log')

        file_object = parser_mediator.GetFileObject(offset=None)
        try:
            self.ParseFileObject(parser_mediator, file_object, **kwargs)
        finally:
            file_object.close()

    def ParseFileObject(self, parser_mediator, file_object, **unused_kwargs):
        """Parses a Windows Prefetch file-like object.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      file_object: A file-like object.

    Raises:
      UnableToParseFile: when the file cannot be parsed.
    """
        file_object.seek(0, os.SEEK_SET)
        file_header = self._ParseFileHeader(file_object)

        try:
            # The struct includes the end-of-string character that we need
            # to strip off.
            description = b''.join(
                file_header.description).decode(u'utf16')[:-1]
        except UnicodeDecodeError as exception:
            description = u''
            logging.warning(
                (u'[{0:s}] Unable to decode description UTF-16 stream with '
                 u'error: {1:s}').format(self.NAME, exception))

        file_object.seek(-8, os.SEEK_END)
        file_footer = self._ParseFileFooter(file_object)

        timestamp = file_footer.get(u'creation_time', 0)
        if not timestamp:
            parser_mediator.ProduceParseError(u'Timestamp not set.')
        else:
            event_object = RestorePointInfoEvent(
                timestamp, file_header.event_type,
                file_header.restore_point_type, file_header.sequence_number,
                description)
            parser_mediator.ProduceEvent(event_object)
Ejemplo n.º 7
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)
Ejemplo n.º 8
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()
Ejemplo n.º 9
0
 construct.RepeatUntil(
     lambda obj, ctx: obj.block_start == 0x3B,
     construct.Struct(
         'body',
         construct.ULInt8('block_start'),
         construct.Embedded(
             construct.Switch(
                 'block',
                 lambda ctx: ctx.block_start,
                 {
                     0x3B:
                     construct.Struct(
                         # workaround for Pass not working
                         'terminator',
                         construct.Value('terminator',
                                         lambda ctx: 'terminator'),
                     ),
                     0x2C:
                     _image_block,
                     0x21:
                     construct.Struct(
                         'ext',
                         construct.ULInt8('ext_label'),
                         construct.Embedded(
                             construct.Switch(
                                 'extension',
                                 lambda ctx: ctx.ext_label,
                                 {
                                     0xFF: _application_extension,
                                     0xFE: _comment_extension,
                                     0xF9: _gce_extension,
                                 },
                                 default=_unknown_extension,
                             ), ),
                     ),
                 },
             ), ),
     ),
 ),
Ejemplo n.º 10
0
            c.ULInt16("busses"),
            c.ULInt16("planes"),
            c.ULInt16("ships"),
        ),
        c.Struct("stations",
            c.ULInt16("trains"),
            c.ULInt16("trucks"),
            c.ULInt16("busses"),
            c.ULInt16("planes"),
            c.ULInt16("ships"),
        ),
        c.ULInt8("has_clients"),
        c.If(lambda ctx: ctx["has_clients"] > 0,
            c.RepeatUntil(lambda obj, ctx: obj.more == 0, c.Struct("clients",
                c.CString("name"),
                c.CString("unique_id"),
                OpenTTD_NewDate("joined"),
                c.ULInt8("more"),
            )),
        ),
    )),
    c.ULInt8("has_spectators"),
    c.If(lambda ctx: ctx["has_spectators"] > 0,
        c.RepeatUntil(lambda obj, ctx: obj.more == 0, c.Struct("spectators",
            c.CString("name"),
            c.CString("unique_id"),
            OpenTTD_NewDate("joined"),
            c.ULInt8("more"),
        )),
    ),
)
Ejemplo n.º 11
0
class BsmParser(interface.FileObjectParser):
    """Parser for BSM files."""

    _INITIAL_FILE_OFFSET = None

    NAME = u'bsm_log'
    DESCRIPTION = u'Parser for BSM log files.'

    # BSM supported version (0x0b = 11).
    AUDIT_HEADER_VERSION = 11

    # Magic Trail Header.
    BSM_TOKEN_TRAILER_MAGIC = b'b105'

    # IP Version constants.
    AU_IPv4 = 4
    AU_IPv6 = 16

    IPV4_STRUCT = construct.UBInt32(u'ipv4')

    IPV6_STRUCT = construct.Struct(u'ipv6', construct.UBInt64(u'high'),
                                   construct.UBInt64(u'low'))

    # Tested structures.
    # INFO: I have ommited the ID in the structures declaration.
    #       I used the BSM_TYPE first to read the ID, and then, the structure.
    # Tokens always start with an ID value that identifies their token
    # type and subsequent structure.
    BSM_TYPE = construct.UBInt8(u'token_id')

    # Data type structures.
    BSM_TOKEN_DATA_CHAR = construct.String(u'value', 1)
    BSM_TOKEN_DATA_SHORT = construct.UBInt16(u'value')
    BSM_TOKEN_DATA_INTEGER = construct.UBInt32(u'value')

    # Common structure used by other structures.
    # audit_uid: integer, uid that generates the entry.
    # effective_uid: integer, the permission user used.
    # effective_gid: integer, the permission group used.
    # real_uid: integer, user id of the user that execute the process.
    # real_gid: integer, group id of the group that execute the process.
    # pid: integer, identification number of the process.
    # session_id: unknown, need research.
    BSM_TOKEN_SUBJECT_SHORT = construct.Struct(
        u'subject_data', construct.UBInt32(u'audit_uid'),
        construct.UBInt32(u'effective_uid'),
        construct.UBInt32(u'effective_gid'), construct.UBInt32(u'real_uid'),
        construct.UBInt32(u'real_gid'), construct.UBInt32(u'pid'),
        construct.UBInt32(u'session_id'))

    # Common structure used by other structures.
    # Identify the kind of inet (IPv4 or IPv6)
    # TODO: instead of 16, AU_IPv6 must be used.
    BSM_IP_TYPE_SHORT = construct.Struct(
        u'bsm_ip_type_short', construct.UBInt32(u'net_type'),
        construct.Switch(u'ip_addr',
                         _BsmTokenGetNetType, {16: IPV6_STRUCT},
                         default=IPV4_STRUCT))

    # Initial fields structure used by header structures.
    # length: integer, the length of the entry, equal to trailer (doc: length).
    # version: integer, version of BSM (AUDIT_HEADER_VERSION).
    # event_type: integer, the type of event (/etc/security/audit_event).
    # modifier: integer, unknown, need research (It is always 0).
    BSM_HEADER = construct.Struct(u'bsm_header', construct.UBInt32(u'length'),
                                  construct.UBInt8(u'version'),
                                  construct.UBInt16(u'event_type'),
                                  construct.UBInt16(u'modifier'))

    # First token of one entry.
    # timestamp: unsigned integer, number of seconds since
    #            January 1, 1970 00:00:00 UTC.
    # microsecond: unsigned integer, number of micro seconds.
    BSM_HEADER32 = construct.Struct(u'bsm_header32', BSM_HEADER,
                                    construct.UBInt32(u'timestamp'),
                                    construct.UBInt32(u'microsecond'))

    BSM_HEADER64 = construct.Struct(u'bsm_header64', BSM_HEADER,
                                    construct.UBInt64(u'timestamp'),
                                    construct.UBInt64(u'microsecond'))

    BSM_HEADER32_EX = construct.Struct(u'bsm_header32_ex', BSM_HEADER,
                                       BSM_IP_TYPE_SHORT,
                                       construct.UBInt32(u'timestamp'),
                                       construct.UBInt32(u'microsecond'))

    # Token TEXT, provides extra information.
    BSM_TOKEN_TEXT = construct.Struct(
        u'bsm_token_text', construct.UBInt16(u'length'),
        construct.Array(_BsmTokenGetLength, construct.UBInt8(u'text')))

    # Path of the executable.
    BSM_TOKEN_PATH = BSM_TOKEN_TEXT

    # Identified the end of the record (follow by TRAILER).
    # status: integer that identifies the status of the exit (BSM_ERRORS).
    # return: returned value from the operation.
    BSM_TOKEN_RETURN32 = construct.Struct(u'bsm_token_return32',
                                          construct.UBInt8(u'status'),
                                          construct.UBInt32(u'return_value'))

    BSM_TOKEN_RETURN64 = construct.Struct(u'bsm_token_return64',
                                          construct.UBInt8(u'status'),
                                          construct.UBInt64(u'return_value'))

    # Identified the number of bytes that was written.
    # magic: 2 bytes that identifies the TRAILER (BSM_TOKEN_TRAILER_MAGIC).
    # length: integer that has the number of bytes from the entry size.
    BSM_TOKEN_TRAILER = construct.Struct(u'bsm_token_trailer',
                                         construct.UBInt16(u'magic'),
                                         construct.UBInt32(u'record_length'))

    # A 32-bits argument.
    # num_arg: the number of the argument.
    # name_arg: the argument's name.
    # text: the string value of the argument.
    BSM_TOKEN_ARGUMENT32 = construct.Struct(
        u'bsm_token_argument32', construct.UBInt8(u'num_arg'),
        construct.UBInt32(u'name_arg'), construct.UBInt16(u'length'),
        construct.Array(_BsmTokenGetLength, construct.UBInt8(u'text')))

    # A 64-bits argument.
    # num_arg: integer, the number of the argument.
    # name_arg: text, the argument's name.
    # text: the string value of the argument.
    BSM_TOKEN_ARGUMENT64 = construct.Struct(
        u'bsm_token_argument64', construct.UBInt8(u'num_arg'),
        construct.UBInt64(u'name_arg'), construct.UBInt16(u'length'),
        construct.Array(_BsmTokenGetLength, construct.UBInt8(u'text')))

    # Identify an user.
    # terminal_id: unknown, research needed.
    # terminal_addr: unknown, research needed.
    BSM_TOKEN_SUBJECT32 = construct.Struct(u'bsm_token_subject32',
                                           BSM_TOKEN_SUBJECT_SHORT,
                                           construct.UBInt32(u'terminal_port'),
                                           IPV4_STRUCT)

    # Identify an user using a extended Token.
    # terminal_port: unknown, need research.
    # net_type: unknown, need research.
    BSM_TOKEN_SUBJECT32_EX = construct.Struct(
        u'bsm_token_subject32_ex', BSM_TOKEN_SUBJECT_SHORT,
        construct.UBInt32(u'terminal_port'), BSM_IP_TYPE_SHORT)

    # au_to_opaque // AUT_OPAQUE
    BSM_TOKEN_OPAQUE = BSM_TOKEN_TEXT

    # au_to_seq // AUT_SEQ
    BSM_TOKEN_SEQUENCE = BSM_TOKEN_DATA_INTEGER

    # Program execution with options.
    # For each argument we are going to have a string+ "\x00".
    # Example: [00 00 00 02][41 42 43 00 42 42 00]
    #          2 Arguments, Arg1: [414243] Arg2: [4242].
    BSM_TOKEN_EXEC_ARGUMENTS = construct.UBInt32(u'number_arguments')

    BSM_TOKEN_EXEC_ARGUMENT = construct.Struct(
        u'bsm_token_exec_argument',
        construct.RepeatUntil(_BsmTokenIsEndOfString,
                              construct.StaticField("text", 1)))

    # au_to_in_addr // AUT_IN_ADDR:
    BSM_TOKEN_ADDR = IPV4_STRUCT

    # au_to_in_addr_ext // AUT_IN_ADDR_EX:
    BSM_TOKEN_ADDR_EXT = construct.Struct(u'bsm_token_addr_ext',
                                          construct.UBInt32(u'net_type'),
                                          IPV6_STRUCT)

    # au_to_ip // AUT_IP:
    # TODO: parse this header in the correct way.
    BSM_TOKEN_IP = construct.String(u'binary_ipv4_add', 20)

    # au_to_ipc // AUT_IPC:
    BSM_TOKEN_IPC = construct.Struct(u'bsm_token_ipc',
                                     construct.UBInt8(u'object_type'),
                                     construct.UBInt32(u'object_id'))

    # au_to_ipc_perm // au_to_ipc_perm
    BSM_TOKEN_IPC_PERM = construct.Struct(
        u'bsm_token_ipc_perm', construct.UBInt32(u'user_id'),
        construct.UBInt32(u'group_id'), construct.UBInt32(u'creator_user_id'),
        construct.UBInt32(u'creator_group_id'),
        construct.UBInt32(u'access_mode'), construct.UBInt32(u'slot_seq'),
        construct.UBInt32(u'key'))

    # au_to_iport // AUT_IPORT:
    BSM_TOKEN_PORT = construct.UBInt16(u'port_number')

    # au_to_file // AUT_OTHER_FILE32:
    BSM_TOKEN_FILE = construct.Struct(
        u'bsm_token_file', construct.UBInt32(u'timestamp'),
        construct.UBInt32(u'microsecond'), construct.UBInt16(u'length'),
        construct.Array(_BsmTokenGetLength, construct.UBInt8(u'text')))

    # au_to_subject64 // AUT_SUBJECT64:
    BSM_TOKEN_SUBJECT64 = construct.Struct(u'bsm_token_subject64',
                                           BSM_TOKEN_SUBJECT_SHORT,
                                           construct.UBInt64(u'terminal_port'),
                                           IPV4_STRUCT)

    # au_to_subject64_ex // AU_IPv4:
    BSM_TOKEN_SUBJECT64_EX = construct.Struct(
        u'bsm_token_subject64_ex', BSM_TOKEN_SUBJECT_SHORT,
        construct.UBInt32(u'terminal_port'),
        construct.UBInt32(u'terminal_type'), BSM_IP_TYPE_SHORT)

    # au_to_process32 // AUT_PROCESS32:
    BSM_TOKEN_PROCESS32 = construct.Struct(u'bsm_token_process32',
                                           BSM_TOKEN_SUBJECT_SHORT,
                                           construct.UBInt32(u'terminal_port'),
                                           IPV4_STRUCT)

    # au_to_process64 // AUT_PROCESS32:
    BSM_TOKEN_PROCESS64 = construct.Struct(u'bsm_token_process64',
                                           BSM_TOKEN_SUBJECT_SHORT,
                                           construct.UBInt64(u'terminal_port'),
                                           IPV4_STRUCT)

    # au_to_process32_ex // AUT_PROCESS32_EX:
    BSM_TOKEN_PROCESS32_EX = construct.Struct(
        u'bsm_token_process32_ex', BSM_TOKEN_SUBJECT_SHORT,
        construct.UBInt32(u'terminal_port'), BSM_IP_TYPE_SHORT)

    # au_to_process64_ex // AUT_PROCESS64_EX:
    BSM_TOKEN_PROCESS64_EX = construct.Struct(
        u'bsm_token_process64_ex', BSM_TOKEN_SUBJECT_SHORT,
        construct.UBInt64(u'terminal_port'), BSM_IP_TYPE_SHORT)

    # au_to_sock_inet32 // AUT_SOCKINET32:
    BSM_TOKEN_AUT_SOCKINET32 = construct.Struct(
        u'bsm_token_aut_sockinet32', construct.UBInt16(u'net_type'),
        construct.UBInt16(u'port_number'), IPV4_STRUCT)

    # Info: checked against the source code of XNU, but not against
    #       real BSM file.
    BSM_TOKEN_AUT_SOCKINET128 = construct.Struct(
        u'bsm_token_aut_sockinet128', construct.UBInt16(u'net_type'),
        construct.UBInt16(u'port_number'), IPV6_STRUCT)

    INET6_ADDR_TYPE = construct.Struct(u'addr_type',
                                       construct.UBInt16(u'ip_type'),
                                       construct.UBInt16(u'source_port'),
                                       construct.UBInt64(u'saddr_high'),
                                       construct.UBInt64(u'saddr_low'),
                                       construct.UBInt16(u'destination_port'),
                                       construct.UBInt64(u'daddr_high'),
                                       construct.UBInt64(u'daddr_low'))

    INET4_ADDR_TYPE = construct.Struct(
        u'addr_type', construct.UBInt16(u'ip_type'),
        construct.UBInt16(u'source_port'),
        construct.UBInt32(u'source_address'),
        construct.UBInt16(u'destination_port'),
        construct.UBInt32(u'destination_address'))

    # au_to_socket_ex // AUT_SOCKET_EX
    # TODO: Change the 26 for unixbsm.BSM_PROTOCOLS.INET6.
    BSM_TOKEN_AUT_SOCKINET32_EX = construct.Struct(
        u'bsm_token_aut_sockinet32_ex', construct.UBInt16(u'socket_domain'),
        construct.UBInt16(u'socket_type'),
        construct.Switch(u'structure_addr_port',
                         _BsmTokenGetSocketDomain, {26: INET6_ADDR_TYPE},
                         default=INET4_ADDR_TYPE))

    # au_to_sock_unix // AUT_SOCKUNIX
    BSM_TOKEN_SOCKET_UNIX = construct.Struct(
        u'bsm_token_au_to_sock_unix', construct.UBInt16(u'family'),
        construct.RepeatUntil(_BsmTokenIsEndOfString,
                              construct.StaticField("path", 1)))

    # au_to_data // au_to_data
    # how to print: bsmtoken.BSM_TOKEN_DATA_PRINT.
    # type: bsmtoken.BSM_TOKEN_DATA_TYPE.
    # unit_count: number of type values.
    # BSM_TOKEN_DATA has a end field = type * unit_count
    BSM_TOKEN_DATA = construct.Struct(u'bsm_token_data',
                                      construct.UBInt8(u'how_to_print'),
                                      construct.UBInt8(u'data_type'),
                                      construct.UBInt8(u'unit_count'))

    # au_to_attr32 // AUT_ATTR32
    BSM_TOKEN_ATTR32 = construct.Struct(
        u'bsm_token_attr32', construct.UBInt32(u'file_mode'),
        construct.UBInt32(u'uid'), construct.UBInt32(u'gid'),
        construct.UBInt32(u'file_system_id'),
        construct.UBInt64(u'file_system_node_id'),
        construct.UBInt32(u'device'))

    # au_to_attr64 // AUT_ATTR64
    BSM_TOKEN_ATTR64 = construct.Struct(
        u'bsm_token_attr64', construct.UBInt32(u'file_mode'),
        construct.UBInt32(u'uid'), construct.UBInt32(u'gid'),
        construct.UBInt32(u'file_system_id'),
        construct.UBInt64(u'file_system_node_id'),
        construct.UBInt64(u'device'))

    # au_to_exit // AUT_EXIT
    BSM_TOKEN_EXIT = construct.Struct(u'bsm_token_exit',
                                      construct.UBInt32(u'status'),
                                      construct.UBInt32(u'return_value'))

    # au_to_newgroups // AUT_NEWGROUPS
    # INFO: we must read BSM_TOKEN_DATA_INTEGER for each group.
    BSM_TOKEN_GROUPS = construct.UBInt16(u'group_number')

    # au_to_exec_env == au_to_exec_args
    BSM_TOKEN_EXEC_ENV = BSM_TOKEN_EXEC_ARGUMENTS

    # au_to_zonename //AUT_ZONENAME
    BSM_TOKEN_ZONENAME = BSM_TOKEN_TEXT

    # Token ID.
    # List of valid Token_ID.
    # Token_ID -> [NAME_STRUCTURE, STRUCTURE]
    # Only the checked structures are been added to the valid structures lists.
    BSM_TYPE_LIST = {
        17: [u'BSM_TOKEN_FILE', BSM_TOKEN_FILE],
        19: [u'BSM_TOKEN_TRAILER', BSM_TOKEN_TRAILER],
        20: [u'BSM_HEADER32', BSM_HEADER32],
        21: [u'BSM_HEADER64', BSM_HEADER64],
        33: [u'BSM_TOKEN_DATA', BSM_TOKEN_DATA],
        34: [u'BSM_TOKEN_IPC', BSM_TOKEN_IPC],
        35: [u'BSM_TOKEN_PATH', BSM_TOKEN_PATH],
        36: [u'BSM_TOKEN_SUBJECT32', BSM_TOKEN_SUBJECT32],
        38: [u'BSM_TOKEN_PROCESS32', BSM_TOKEN_PROCESS32],
        39: [u'BSM_TOKEN_RETURN32', BSM_TOKEN_RETURN32],
        40: [u'BSM_TOKEN_TEXT', BSM_TOKEN_TEXT],
        41: [u'BSM_TOKEN_OPAQUE', BSM_TOKEN_OPAQUE],
        42: [u'BSM_TOKEN_ADDR', BSM_TOKEN_ADDR],
        43: [u'BSM_TOKEN_IP', BSM_TOKEN_IP],
        44: [u'BSM_TOKEN_PORT', BSM_TOKEN_PORT],
        45: [u'BSM_TOKEN_ARGUMENT32', BSM_TOKEN_ARGUMENT32],
        47: [u'BSM_TOKEN_SEQUENCE', BSM_TOKEN_SEQUENCE],
        96: [u'BSM_TOKEN_ZONENAME', BSM_TOKEN_ZONENAME],
        113: [u'BSM_TOKEN_ARGUMENT64', BSM_TOKEN_ARGUMENT64],
        114: [u'BSM_TOKEN_RETURN64', BSM_TOKEN_RETURN64],
        116: [u'BSM_HEADER32_EX', BSM_HEADER32_EX],
        119: [u'BSM_TOKEN_PROCESS64', BSM_TOKEN_PROCESS64],
        122: [u'BSM_TOKEN_SUBJECT32_EX', BSM_TOKEN_SUBJECT32_EX],
        127: [u'BSM_TOKEN_AUT_SOCKINET32_EX', BSM_TOKEN_AUT_SOCKINET32_EX],
        128: [u'BSM_TOKEN_AUT_SOCKINET32', BSM_TOKEN_AUT_SOCKINET32]
    }

    # Untested structures.
    # When not tested structure is found, we try to parse using also
    # these structures.
    BSM_TYPE_LIST_NOT_TESTED = {
        49: [u'BSM_TOKEN_ATTR32', BSM_TOKEN_ATTR32],
        50: [u'BSM_TOKEN_IPC_PERM', BSM_TOKEN_IPC_PERM],
        52: [u'BSM_TOKEN_GROUPS', BSM_TOKEN_GROUPS],
        59: [u'BSM_TOKEN_GROUPS', BSM_TOKEN_GROUPS],
        60: [u'BSM_TOKEN_EXEC_ARGUMENTS', BSM_TOKEN_EXEC_ARGUMENTS],
        61: [u'BSM_TOKEN_EXEC_ENV', BSM_TOKEN_EXEC_ENV],
        62: [u'BSM_TOKEN_ATTR32', BSM_TOKEN_ATTR32],
        82: [u'BSM_TOKEN_EXIT', BSM_TOKEN_EXIT],
        115: [u'BSM_TOKEN_ATTR64', BSM_TOKEN_ATTR64],
        117: [u'BSM_TOKEN_SUBJECT64', BSM_TOKEN_SUBJECT64],
        123: [u'BSM_TOKEN_PROCESS32_EX', BSM_TOKEN_PROCESS32_EX],
        124: [u'BSM_TOKEN_PROCESS64_EX', BSM_TOKEN_PROCESS64_EX],
        125: [u'BSM_TOKEN_SUBJECT64_EX', BSM_TOKEN_SUBJECT64_EX],
        126: [u'BSM_TOKEN_ADDR_EXT', BSM_TOKEN_ADDR_EXT],
        129: [u'BSM_TOKEN_AUT_SOCKINET128', BSM_TOKEN_AUT_SOCKINET128],
        130: [u'BSM_TOKEN_SOCKET_UNIX', BSM_TOKEN_SOCKET_UNIX]
    }

    def __init__(self):
        """Initializes a parser object."""
        super(BsmParser, self).__init__()
        # Create the dictionary with all token IDs: tested and untested.
        self.bsm_type_list_all = self.BSM_TYPE_LIST.copy()
        self.bsm_type_list_all.update(self.BSM_TYPE_LIST_NOT_TESTED)

    def _CopyByteArrayToBase16String(self, byte_array):
        """Copies a byte array into a base-16 encoded Unicode string.

    Args:
      byte_array: A byte array.

    Returns:
      A base-16 encoded Unicode string.
    """
        return u''.join([u'{0:02x}'.format(byte) for byte in byte_array])

    def _CopyUtf8ByteArrayToString(self, byte_array):
        """Copies a UTF-8 encoded byte array into a Unicode string.

    Args:
      byte_array: A byte array containing an UTF-8 encoded string.

    Returns:
      A Unicode string.
    """
        byte_stream = b''.join(map(chr, byte_array))

        try:
            string = byte_stream.decode(u'utf-8')
        except UnicodeDecodeError:
            logging.warning(u'Unable to decode UTF-8 formatted byte array.')
            string = byte_stream.decode(u'utf-8', errors=u'ignore')

        string, _, _ = string.partition(b'\x00')
        return string

    def _IPv4Format(self, address):
        """Change an integer IPv4 address value for its 4 octets representation.

    Args:
      address: integer with the IPv4 address.

    Returns:
      IPv4 address in 4 octet representation (class A, B, C, D).
    """
        ipv4_string = self.IPV4_STRUCT.build(address)
        return socket.inet_ntoa(ipv4_string)

    def _IPv6Format(self, high, low):
        """Provide a readable IPv6 IP having the high and low part in 2 integers.

    Args:
      high: 64 bits integers number with the high part of the IPv6.
      low: 64 bits integers number with the low part of the IPv6.

    Returns:
      String with a well represented IPv6.
    """
        ipv6_string = self.IPV6_STRUCT.build(
            construct.Container(high=high, low=low))
        # socket.inet_ntop not supported in Windows.
        if hasattr(socket, u'inet_ntop'):
            return socket.inet_ntop(socket.AF_INET6, ipv6_string)

        # TODO: this approach returns double "::", illegal IPv6 addr.
        str_address = binascii.hexlify(ipv6_string)
        address = []
        blank = False
        for pos in range(0, len(str_address), 4):
            if str_address[pos:pos + 4] == u'0000':
                if not blank:
                    address.append(u'')
                    blank = True
            else:
                blank = False
                address.append(str_address[pos:pos + 4].lstrip(u'0'))
        return u':'.join(address)

    def _RawToUTF8(self, byte_stream):
        """Copies a UTF-8 byte stream into a Unicode string.

    Args:
      byte_stream: A byte stream containing an UTF-8 encoded string.

    Returns:
      A Unicode string.
    """
        try:
            string = byte_stream.decode(u'utf-8')
        except UnicodeDecodeError:
            logging.warning(
                u'Decode UTF8 failed, the message string may be cut short.')
            string = byte_stream.decode(u'utf-8', errors=u'ignore')
        return string.partition(b'\x00')[0]

    def ParseFileObject(self, parser_mediator, file_object, **kwargs):
        """Parses a BSM file-like object.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      file_object: A file-like object.

    Raises:
      UnableToParseFile: when the file cannot be parsed.
    """
        file_object.seek(0, os.SEEK_SET)

        try:
            is_bsm = self.VerifyFile(parser_mediator, file_object)
        except (IOError, construct.FieldError) as exception:
            raise errors.UnableToParseFile(
                u'Unable to parse BSM file with error: {0:s}'.format(
                    exception))

        if not is_bsm:
            raise errors.UnableToParseFile(u'Not a BSM File, unable to parse.')

        event_object = self.ReadBSMEvent(parser_mediator, file_object)
        while event_object:
            parser_mediator.ProduceEvent(event_object)

            event_object = self.ReadBSMEvent(parser_mediator, file_object)

    def ReadBSMEvent(self, parser_mediator, file_object):
        """Returns a BsmEvent from a single BSM entry.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      file_object: A file-like object.

    Returns:
      An event object.
    """
        # A list of tokens that has the entry.
        extra_tokens = []

        offset = file_object.tell()

        # Token header, first token for each entry.
        try:
            token_id = self.BSM_TYPE.parse_stream(file_object)
        except (IOError, construct.FieldError):
            return

        bsm_type, structure = self.BSM_TYPE_LIST.get(token_id, [u'', u''])
        if bsm_type == u'BSM_HEADER32':
            token = structure.parse_stream(file_object)
        elif bsm_type == u'BSM_HEADER64':
            token = structure.parse_stream(file_object)
        elif bsm_type == u'BSM_HEADER32_EX':
            token = structure.parse_stream(file_object)
        else:
            logging.warning(
                u'Token ID Header {0} not expected at position 0x{1:X}.'
                u'The parsing of the file cannot be continued'.format(
                    token_id, file_object.tell()))
            # TODO: if it is a Mac OS X, search for the trailer magic value
            #       as a end of the entry can be a possibility to continue.
            return

        length = token.bsm_header.length
        event_type = u'{0} ({1})'.format(
            bsmtoken.BSM_AUDIT_EVENT.get(token.bsm_header.event_type,
                                         u'UNKNOWN'),
            token.bsm_header.event_type)
        timestamp = timelib.Timestamp.FromPosixTimeWithMicrosecond(
            token.timestamp, token.microsecond)

        # Read until we reach the end of the record.
        while file_object.tell() < (offset + length):
            # Check if it is a known token.
            try:
                token_id = self.BSM_TYPE.parse_stream(file_object)
            except (IOError, construct.FieldError):
                logging.warning(
                    u'Unable to parse the Token ID at position: {0:d}'.format(
                        file_object.tell()))
                return
            if not token_id in self.BSM_TYPE_LIST:
                pending = (offset + length) - file_object.tell()
                extra_tokens.extend(
                    self.TryWithUntestedStructures(file_object, token_id,
                                                   pending))
            else:
                token = self.BSM_TYPE_LIST[token_id][1].parse_stream(
                    file_object)
                extra_tokens.append(
                    self.FormatToken(token_id, token, file_object))

        if file_object.tell() > (offset + length):
            logging.warning(u'Token ID {0} not expected at position 0x{1:X}.'
                            u'Jumping for the next entry.'.format(
                                token_id, file_object.tell()))
            try:
                file_object.seek((offset + length) - file_object.tell(),
                                 os.SEEK_CUR)
            except (IOError, construct.FieldError) as exception:
                logging.warning(
                    u'Unable to jump to next entry with error: {0:s}'.format(
                        exception))
                return

        # BSM can be in more than one OS: BSD, Solaris and Mac OS X.
        if parser_mediator.platform == u'MacOSX':
            # In Mac OS X the last two tokens are the return status and the trailer.
            if len(extra_tokens) >= 2:
                return_value = extra_tokens[-2:-1][0]
                if (return_value.startswith(u'[BSM_TOKEN_RETURN32')
                        or return_value.startswith(u'[BSM_TOKEN_RETURN64')):
                    _ = extra_tokens.pop(len(extra_tokens) - 2)
                else:
                    return_value = u'Return unknown'
            else:
                return_value = u'Return unknown'
            if extra_tokens:
                trailer = extra_tokens[-1]
                if trailer.startswith(u'[BSM_TOKEN_TRAILER'):
                    _ = extra_tokens.pop(len(extra_tokens) - 1)
                else:
                    trailer = u'Trailer unknown'
            else:
                trailer = u'Trailer unknown'
            return MacBsmEvent(event_type, timestamp, u'. '.join(extra_tokens),
                               return_value, trailer, offset)
        else:
            # Generic BSM format.
            if extra_tokens:
                trailer = extra_tokens[-1]
                if trailer.startswith(u'[BSM_TOKEN_TRAILER'):
                    _ = extra_tokens.pop(len(extra_tokens) - 1)
                else:
                    trailer = u'Trailer unknown'
            else:
                trailer = u'Trailer unknown'
            return BsmEvent(event_type, timestamp, u'. '.join(extra_tokens),
                            trailer, offset)

    def VerifyFile(self, parser_mediator, file_object):
        """Check if the file is a BSM file.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      file_event: file that we want to check.

    Returns:
      True if this is a valid BSM file, otherwise False.
    """
        if file_object.tell() != 0:
            file_object.seek(0)

        # First part of the entry is always a Header.
        try:
            token_id = self.BSM_TYPE.parse_stream(file_object)
        except (IOError, construct.FieldError):
            return False
        if token_id not in self.BSM_TYPE_LIST:
            return False

        bsm_type, structure = self.BSM_TYPE_LIST.get(token_id, [u'', u''])
        try:
            if bsm_type == u'BSM_HEADER32':
                header = structure.parse_stream(file_object)
            elif bsm_type == u'BSM_HEADER64':
                header = structure.parse_stream(file_object)
            elif bsm_type == u'BSM_HEADER32_EX':
                header = structure.parse_stream(file_object)
            else:
                return False
        except (IOError, construct.FieldError):
            return False
        if header.bsm_header.version != self.AUDIT_HEADER_VERSION:
            return False

        try:
            token_id = self.BSM_TYPE.parse_stream(file_object)
        except (IOError, construct.FieldError):
            return False

        # If is Mac OS X BSM file, next entry is a  text token indicating
        # if it is a normal start or it is a recovery track.
        if parser_mediator.platform == u'MacOSX':
            bsm_type_list = self.BSM_TYPE_LIST.get(token_id)
            if not bsm_type_list:
                return False

            if bsm_type_list[0] != u'BSM_TOKEN_TEXT':
                logging.warning(
                    u'It is not a valid first entry for Mac OS X BSM.')
                return False
            try:
                token = self.BSM_TOKEN_TEXT.parse_stream(file_object)
            except (IOError, construct.FieldError):
                return

            text = self._CopyUtf8ByteArrayToString(token.text)
            if (text != u'launchctl::Audit startup'
                    and text != u'launchctl::Audit recovery'):
                logging.warning(
                    u'It is not a valid first entry for Mac OS X BSM.')
                return False

        file_object.seek(0)
        return True

    def TryWithUntestedStructures(self, file_object, token_id, pending):
        """Try to parse the pending part of the entry using untested structures.

    Args:
      file_object: BSM file.
      token_id: integer with the id that comes from the unknown token.
      pending: pending length of the entry.

    Returns:
      A list of extra tokens data that can be parsed using non-tested
      structures. A message indicating that a structure cannot be parsed
      is added for unparsed structures.
    """
        # Data from the unknown structure.
        start_position = file_object.tell()
        start_token_id = token_id
        extra_tokens = []

        # Read all the "pending" bytes.
        try:
            if token_id in self.bsm_type_list_all:
                token = self.bsm_type_list_all[token_id][1].parse_stream(
                    file_object)
                extra_tokens.append(
                    self.FormatToken(token_id, token, file_object))
                while file_object.tell() < (start_position + pending):
                    # Check if it is a known token.
                    try:
                        token_id = self.BSM_TYPE.parse_stream(file_object)
                    except (IOError, construct.FieldError):
                        logging.warning(
                            u'Unable to parse the Token ID at position: {0:d}'.
                            format(file_object.tell()))
                        return
                    if token_id not in self.bsm_type_list_all:
                        break
                    token = self.bsm_type_list_all[token_id][1].parse_stream(
                        file_object)
                    extra_tokens.append(
                        self.FormatToken(token_id, token, file_object))
        except (IOError, construct.FieldError):
            token_id = 255

        next_entry = (start_position + pending)
        if file_object.tell() != next_entry:
            # Unknown Structure.
            logging.warning(
                u'Unknown Token at "0x{0:X}", ID: {1} (0x{2:X})'.format(
                    start_position - 1, token_id, token_id))
            # TODO: another way to save this information must be found.
            extra_tokens.append(u'Plaso: some tokens from this entry can '
                                u'not be saved. Entry at 0x{0:X} with unknown '
                                u'token id "0x{1:X}".'.format(
                                    start_position - 1, start_token_id))
            # Move to next entry.
            file_object.seek(next_entry - file_object.tell(), os.SEEK_CUR)
            # It returns null list because it doesn't know witch structure was
            # the incorrect structure that makes that it can arrive to the spected
            # end of the entry.
            return []
        return extra_tokens

    # TODO: instead of compare the text to know what structure was parsed
    #       is better to compare directly the numeric number (token_id),
    #       less readable, but better performance.
    def FormatToken(self, token_id, token, file_object):
        """Parse the Token depending of the type of the structure.

    Args:
      token_id: Identification integer of the token_type.
      token: Token struct to parse.
      file_object: BSM file.

    Returns:
      String with the parsed Token values.
    """
        if token_id not in self.bsm_type_list_all:
            return u'Type Unknown: {0:d} (0x{0:X})'.format(token_id)

        bsm_type, _ = self.bsm_type_list_all.get(token_id, [u'', u''])

        if bsm_type in [
                u'BSM_TOKEN_TEXT', u'BSM_TOKEN_PATH', u'BSM_TOKEN_ZONENAME'
        ]:
            try:
                string = self._CopyUtf8ByteArrayToString(token.text)
            except TypeError:
                string = u'Unknown'
            return u'[{0}: {1:s}]'.format(bsm_type, string)

        elif bsm_type in [
                u'BSM_TOKEN_RETURN32', u'BSM_TOKEN_RETURN64', u'BSM_TOKEN_EXIT'
        ]:
            return u'[{0}: {1} ({2}), System call status: {3}]'.format(
                bsm_type, bsmtoken.BSM_ERRORS.get(token.status, u'Unknown'),
                token.status, token.return_value)

        elif bsm_type in [u'BSM_TOKEN_SUBJECT32', u'BSM_TOKEN_SUBJECT64']:
            return (
                u'[{0}: aid({1}), euid({2}), egid({3}), uid({4}), gid({5}), '
                u'pid({6}), session_id({7}), terminal_port({8}), '
                u'terminal_ip({9})]').format(
                    bsm_type, token.subject_data.audit_uid,
                    token.subject_data.effective_uid,
                    token.subject_data.effective_gid,
                    token.subject_data.real_uid, token.subject_data.real_gid,
                    token.subject_data.pid, token.subject_data.session_id,
                    token.terminal_port, self._IPv4Format(token.ipv4))

        elif bsm_type in [
                u'BSM_TOKEN_SUBJECT32_EX', u'BSM_TOKEN_SUBJECT64_EX'
        ]:
            if token.bsm_ip_type_short.net_type == self.AU_IPv6:
                ip = self._IPv6Format(token.bsm_ip_type_short.ip_addr.high,
                                      token.bsm_ip_type_short.ip_addr.low)
            elif token.bsm_ip_type_short.net_type == self.AU_IPv4:
                ip = self._IPv4Format(token.bsm_ip_type_short.ip_addr)
            else:
                ip = u'unknown'
            return (
                u'[{0}: aid({1}), euid({2}), egid({3}), uid({4}), gid({5}), '
                u'pid({6}), session_id({7}), terminal_port({8}), '
                u'terminal_ip({9})]').format(
                    bsm_type, token.subject_data.audit_uid,
                    token.subject_data.effective_uid,
                    token.subject_data.effective_gid,
                    token.subject_data.real_uid, token.subject_data.real_gid,
                    token.subject_data.pid, token.subject_data.session_id,
                    token.terminal_port, ip)

        elif bsm_type in [u'BSM_TOKEN_ARGUMENT32', u'BSM_TOKEN_ARGUMENT64']:
            string = self._CopyUtf8ByteArrayToString(token.text)
            return u'[{0}: {1:s}({2}) is 0x{3:X}]'.format(
                bsm_type, string, token.num_arg, token.name_arg)

        elif bsm_type in [u'BSM_TOKEN_EXEC_ARGUMENTS', u'BSM_TOKEN_EXEC_ENV']:
            arguments = []
            for _ in range(0, token):
                sub_token = self.BSM_TOKEN_EXEC_ARGUMENT.parse_stream(
                    file_object)
                string = self._CopyUtf8ByteArrayToString(sub_token.text)
                arguments.append(string)
            return u'[{0}: {1:s}]'.format(bsm_type, u' '.join(arguments))

        elif bsm_type == u'BSM_TOKEN_AUT_SOCKINET32':
            return (u'[{0}: {1} ({2}) open in port {3}. Address {4}]'.format(
                bsm_type, bsmtoken.BSM_PROTOCOLS.get(token.net_type,
                                                     u'UNKNOWN'),
                token.net_type, token.port_number,
                self._IPv4Format(token.ipv4)))

        elif bsm_type == u'BSM_TOKEN_AUT_SOCKINET128':
            return u'[{0}: {1} ({2}) open in port {3}. Address {4}]'.format(
                bsm_type, bsmtoken.BSM_PROTOCOLS.get(token.net_type,
                                                     u'UNKNOWN'),
                token.net_type, token.port_number,
                self._IPv6Format(token.ipv6.high, token.ipv6.low))

        elif bsm_type == u'BSM_TOKEN_ADDR':
            return u'[{0}: {1}]'.format(bsm_type, self._IPv4Format(token))

        elif bsm_type == u'BSM_TOKEN_IP':
            return u'[IPv4_Header: 0x{0:s}]'.format(token.encode(u'hex'))

        elif bsm_type == u'BSM_TOKEN_ADDR_EXT':
            return u'[{0}: {1} ({2}). Address {3}]'.format(
                bsm_type, bsmtoken.BSM_PROTOCOLS.get(token.net_type,
                                                     u'UNKNOWN'),
                token.net_type,
                self._IPv6Format(token.ipv6.high, token.ipv6.low))

        elif bsm_type == u'BSM_TOKEN_PORT':
            return u'[{0}: {1}]'.format(bsm_type, token)

        elif bsm_type == u'BSM_TOKEN_TRAILER':
            return u'[{0}: {1}]'.format(bsm_type, token.record_length)

        elif bsm_type == u'BSM_TOKEN_FILE':
            # TODO: if this timestamp is usefull, it must be extracted as a separate
            #       event object.
            timestamp = timelib.Timestamp.FromPosixTimeWithMicrosecond(
                token.timestamp, token.microsecond)
            date_time = timelib.Timestamp.CopyToDatetime(timestamp, pytz.UTC)
            date_time_string = date_time.strftime(u'%Y-%m-%d %H:%M:%S')

            string = self._CopyUtf8ByteArrayToString(token.text)
            return u'[{0}: {1:s}, timestamp: {2:s}]'.format(
                bsm_type, string, date_time_string)

        elif bsm_type == u'BSM_TOKEN_IPC':
            return u'[{0}: object type {1}, object id {2}]'.format(
                bsm_type, token.object_type, token.object_id)

        elif bsm_type in [u'BSM_TOKEN_PROCESS32', u'BSM_TOKEN_PROCESS64']:
            return (
                u'[{0}: aid({1}), euid({2}), egid({3}), uid({4}), gid({5}), '
                u'pid({6}), session_id({7}), terminal_port({8}), '
                u'terminal_ip({9})]').format(
                    bsm_type, token.subject_data.audit_uid,
                    token.subject_data.effective_uid,
                    token.subject_data.effective_gid,
                    token.subject_data.real_uid, token.subject_data.real_gid,
                    token.subject_data.pid, token.subject_data.session_id,
                    token.terminal_port, self._IPv4Format(token.ipv4))

        elif bsm_type in [
                u'BSM_TOKEN_PROCESS32_EX', u'BSM_TOKEN_PROCESS64_EX'
        ]:
            if token.bsm_ip_type_short.net_type == self.AU_IPv6:
                ip = self._IPv6Format(token.bsm_ip_type_short.ip_addr.high,
                                      token.bsm_ip_type_short.ip_addr.low)
            elif token.bsm_ip_type_short.net_type == self.AU_IPv4:
                ip = self._IPv4Format(token.bsm_ip_type_short.ip_addr)
            else:
                ip = u'unknown'
            return (
                u'[{0}: aid({1}), euid({2}), egid({3}), uid({4}), gid({5}), '
                u'pid({6}), session_id({7}), terminal_port({8}), '
                u'terminal_ip({9})]').format(
                    bsm_type, token.subject_data.audit_uid,
                    token.subject_data.effective_uid,
                    token.subject_data.effective_gid,
                    token.subject_data.real_uid, token.subject_data.real_gid,
                    token.subject_data.pid, token.subject_data.session_id,
                    token.terminal_port, ip)

        elif bsm_type == u'BSM_TOKEN_DATA':
            data = []
            data_type = bsmtoken.BSM_TOKEN_DATA_TYPE.get(token.data_type, u'')
            if data_type == u'AUR_CHAR':
                for _ in range(token.unit_count):
                    data.append(
                        self.BSM_TOKEN_DATA_CHAR.parse_stream(file_object))
            elif data_type == u'AUR_SHORT':
                for _ in range(token.unit_count):
                    data.append(
                        self.BSM_TOKEN_DAT_SHORT.parse_stream(file_object))
            elif data_type == u'AUR_INT32':
                for _ in range(token.unit_count):
                    data.append(
                        self.BSM_TOKEN_DATA_INTEGER.parse_stream(file_object))
            else:
                data.append(u'Unknown type data')
            # TODO: the data when it is string ends with ".", HW a space is return
            #       after uses the UTF-8 conversion.
            return u'[{0}: Format data: {1}, Data: {2}]'.format(
                bsm_type, bsmtoken.BSM_TOKEN_DATA_PRINT[token.how_to_print],
                self._RawToUTF8(u''.join(data)))

        elif bsm_type in [u'BSM_TOKEN_ATTR32', u'BSM_TOKEN_ATTR64']:
            return (u'[{0}: Mode: {1}, UID: {2}, GID: {3}, '
                    u'File system ID: {4}, Node ID: {5}, Device: {6}]').format(
                        bsm_type, token.file_mode, token.uid, token.gid,
                        token.file_system_id, token.file_system_node_id,
                        token.device)

        elif bsm_type == u'BSM_TOKEN_GROUPS':
            arguments = []
            for _ in range(token):
                arguments.append(
                    self._RawToUTF8(
                        self.BSM_TOKEN_DATA_INTEGER.parse_stream(file_object)))
            return u'[{0}: {1:s}]'.format(bsm_type, u','.join(arguments))

        elif bsm_type == u'BSM_TOKEN_AUT_SOCKINET32_EX':
            if bsmtoken.BSM_PROTOCOLS.get(token.socket_domain,
                                          u'') == u'INET6':
                saddr = self._IPv6Format(token.structure_addr_port.saddr_high,
                                         token.structure_addr_port.saddr_low)
                daddr = self._IPv6Format(token.structure_addr_port.daddr_high,
                                         token.structure_addr_port.daddr_low)
            else:
                saddr = self._IPv4Format(
                    token.structure_addr_port.source_address)
                daddr = self._IPv4Format(
                    token.structure_addr_port.destination_address)

            return u'[{0}: from {1} port {2} to {3} port {4}]'.format(
                bsm_type, saddr, token.structure_addr_port.source_port, daddr,
                token.structure_addr_port.destination_port)

        elif bsm_type == u'BSM_TOKEN_IPC_PERM':
            return (u'[{0}: user id {1}, group id {2}, create user id {3}, '
                    u'create group id {4}, access {5}]').format(
                        bsm_type, token.user_id, token.group_id,
                        token.creator_user_id, token.creator_group_id,
                        token.access_mode)

        elif bsm_type == u'BSM_TOKEN_SOCKET_UNIX':
            string = self._CopyUtf8ByteArrayToString(token.path)
            return u'[{0}: Family {1}, Path {2:s}]'.format(
                bsm_type, token.family, string)

        elif bsm_type == u'BSM_TOKEN_OPAQUE':
            string = self._CopyByteArrayToBase16String(token.text)
            return u'[{0}: {1:s}]'.format(bsm_type, string)

        elif bsm_type == u'BSM_TOKEN_SEQUENCE':
            return u'[{0}: {1}]'.format(bsm_type, token)
Ejemplo n.º 12
0
class FlacReader:
    FRAME_HEADER = construct.Struct(
        'frame_header', construct.Bits('sync', 14),
        construct.Bits('reserved', 2), construct.Bits('block_size', 4),
        construct.Bits('sample_rate', 4),
        construct.Bits('channel_assignment', 4),
        construct.Bits('bits_per_sample', 3), construct.Padding(1),
        construct.IfThenElse(
            'total_channels', lambda ctx1: ctx1['channel_assignment'] <= 7,
            construct.Value('c', lambda ctx2: ctx2['channel_assignment'] + 1),
            construct.Value('c', lambda ctx3: 2)), UTF8('frame_number'),
        construct.IfThenElse(
            'extended_block_size', lambda ctx1: ctx1['block_size'] == 6,
            construct.Bits('b', 8),
            construct.If(lambda ctx2: ctx2['block_size'] == 7,
                         construct.Bits('b', 16))),
        construct.IfThenElse(
            'extended_sample_rate', lambda ctx1: ctx1['sample_rate'] == 12,
            construct.Bits('s', 8),
            construct.If(lambda ctx2: ctx2['sample_rate'] in (13, 14),
                         construct.Bits('s', 16))), construct.Bits('crc8', 8))

    UNARY = construct.Struct(
        'unary',
        construct.RepeatUntil(lambda obj, ctx: obj == '\x01',
                              construct.Field('bytes', 1)),
        construct.Value('value', lambda ctx: len(ctx['bytes']) - 1))

    SUBFRAME_HEADER = construct.Struct(
        'subframe_header', construct.Padding(1),
        construct.Bits('subframe_type', 6),
        construct.Flag('has_wasted_bits_per_sample'),
        construct.IfThenElse('wasted_bits_per_sample',
                             lambda ctx: ctx['has_wasted_bits_per_sample'],
                             PlusOne(Unary('value')),
                             construct.Value('value', lambda ctx2: 0)))

    GET_BLOCKSIZE_FROM_STREAMINFO = -1
    GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER = -2
    GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER = -3

    BLOCK_SIZE = (GET_BLOCKSIZE_FROM_STREAMINFO, 192, 576, 1152, 2304, 4608,
                  GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER,
                  GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER, 256, 512, 1024, 2048,
                  4096, 8192, 16384, 32768)

    GET_SAMPLE_SIZE_FROM_STREAMINFO = -1
    SAMPLE_SIZE = (GET_SAMPLE_SIZE_FROM_STREAMINFO, 8, 12, None, 16, 20, 24,
                   None)

    def FIXED0(subframe, residual, i):
        subframe.insert(i, residual[i])

    def FIXED1(subframe, residual, i):
        subframe.insert(i, subframe[i - 1] + residual[i])

    def FIXED2(subframe, residual, i):
        subframe.insert(i,
                        ((2 * subframe[i - 1]) - subframe[i - 2] + \
                         residual[i]))

    def FIXED3(subframe, residual, i):
        subframe.insert(i,
                        ((3 * subframe[i - 1]) - (3 * subframe[i - 2]) + \
                         subframe[i - 3] + residual[i]))

    def FIXED4(subframe, residual, i):
        subframe.insert(i,
                        ((4 * subframe[i - 1]) - (6 * subframe[i - 2]) + \
                         (4 * subframe[i - 3]) - subframe[i - 4] + residual[i]))

    #iterates over all of the channels, in order
    def MERGE_INDEPENDENT(channel_list):
        channel_data = [iter(c) for c in channel_list]

        while (True):
            for channel in channel_data:
                yield channel.next()

    def MERGE_LEFT(channel_list):
        channel_left = iter(channel_list[0])
        channel_side = iter(channel_list[1])

        while (True):
            left = channel_left.next()
            side = channel_side.next()

            yield left
            yield left - side

    def MERGE_RIGHT(channel_list):
        channel_side = iter(channel_list[0])
        channel_right = iter(channel_list[1])

        while (True):
            side = channel_side.next()
            right = channel_right.next()

            yield side + right
            yield right

    def MERGE_MID(channel_list):
        channel_mid = iter(channel_list[0])
        channel_side = iter(channel_list[1])

        while (True):
            mid = channel_mid.next()
            side = channel_side.next()

            mid = mid << 1
            mid |= (side & 0x1)

            yield (mid + side) >> 1
            yield (mid - side) >> 1

    CHANNEL_FUNCTIONS = (MERGE_INDEPENDENT, MERGE_INDEPENDENT,
                         MERGE_INDEPENDENT, MERGE_INDEPENDENT,
                         MERGE_INDEPENDENT, MERGE_INDEPENDENT,
                         MERGE_INDEPENDENT, MERGE_INDEPENDENT, MERGE_LEFT,
                         MERGE_RIGHT, MERGE_MID)

    FIXED_FUNCTIONS = (FIXED0, FIXED1, FIXED2, FIXED3, FIXED4)

    def __init__(self, flac_stream):
        self.stream = BufferedStream(flac_stream)
        self.streaminfo = None
        self.bitstream = None

        #ensure the file starts with 'fLaC'
        self.read_stream_marker()

        #initialize self.bitstream
        self.begin_bitstream()

        #find self.streaminfo in case we need it
        self.read_metadata_blocks()

    def close(self):
        if (self.bitstream != None):
            self.bitstream.close()
        else:
            self.stream.close()

    def read_stream_marker(self):
        if (self.stream.read(4) != 'fLaC'):
            raise FlacStreamException('invalid stream marker')

    def read_metadata_blocks(self):
        block = audiotools.FlacAudio.METADATA_BLOCK_HEADER.parse_stream(
            self.stream)
        while (block.last_block == 0):
            if (block.block_type == 0):
                self.streaminfo = audiotools.FlacAudio.STREAMINFO.parse_stream(
                    self.stream)
            else:
                self.stream.seek(block.block_length, 1)

            block = audiotools.FlacAudio.METADATA_BLOCK_HEADER.parse_stream(
                self.stream)
        self.stream.seek(block.block_length, 1)

    def begin_bitstream(self):
        import bitstream

        #self.bitstream = construct.BitStreamReader(self.stream)
        self.bitstream = bitstream.BitStreamReader(self.stream)

    def read_frame(self):
        self.stream.reset_buffer()

        try:
            header = FlacReader.FRAME_HEADER.parse_stream(self.bitstream)
        except construct.core.FieldError:
            return ""

        if (header.sync != 0x3FFE):
            raise FlacStreamException('invalid sync')

        if (crc8(self.stream.getvalue()[0:-1]) != header.crc8):
            raise FlacStreamException('crc8 checksum failed')

        #block_size tells us how many samples we need from each subframe
        block_size = FlacReader.BLOCK_SIZE[header.block_size]
        if (block_size == self.GET_BLOCKSIZE_FROM_STREAMINFO):
            block_size = self.streaminfo.maximum_blocksize

        elif ((block_size == self.GET_8BIT_BLOCKSIZE_FROM_END_OF_HEADER)
              or (block_size == self.GET_16BIT_BLOCKSIZE_FROM_END_OF_HEADER)):
            block_size = header.extended_block_size + 1

        #grab subframe data as 32-bit array objects
        subframe_data = []

        for channel_number in xrange(header.total_channels):
            subframe_data.append(
                self.read_subframe(header, block_size, channel_number))

        crc16sum = crc16(self.stream.getvalue())

        #try to byte-align the stream
        if (len(self.bitstream.buffer) > 0):
            self.bitstream.read(len(self.bitstream.buffer))

        if (crc16sum != construct.Bits('crc16', 16).parse_stream(
                self.bitstream)):
            raise FlacStreamException('crc16 checksum failed')

        #convert our list of subframe data arrays into
        #a string of sample data
        if (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == 16):
            merged_frames = array.array(
                'h', FlacReader.CHANNEL_FUNCTIONS[header.channel_assignment](
                    subframe_data))

            if (audiotools.BIG_ENDIAN):
                merged_frames.byteswap()

            return merged_frames.tostring()

        elif (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == 8):
            merged_frames = array.array(
                'b', FlacReader.CHANNEL_FUNCTIONS[header.channel_assignment](
                    subframe_data))

            return merged_frames.tostring()

        else:
            if (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == \
                self.GET_SAMPLE_SIZE_FROM_STREAMINFO):
                bits_per_sample = self.streaminfo.bits_per_sample + 1

            elif (FlacReader.SAMPLE_SIZE[header.bits_per_sample] == None):
                raise FlacStreamException('invalid bits per sample')

            else:
                bits_per_sample = FlacReader.SAMPLE_SIZE[
                    header.bits_per_sample]

            stream = construct.GreedyRepeater(
                construct.BitStruct(
                    'bits',
                    construct.Bits('value',
                                   bits_per_sample,
                                   swapped=True,
                                   signed=True)))

            return stream.build([
                construct.Container(value=v)
                for v in FlacReader.CHANNEL_FUNCTIONS[
                    header.channel_assignment](subframe_data)
            ])

    def read_subframe(self, frame_header, block_size, channel_number):
        subframe_header = \
                        FlacReader.SUBFRAME_HEADER.parse_stream(self.bitstream)

        #figure out the bits-per-sample of this subframe
        if ((frame_header.channel_assignment == 8) and (channel_number == 1)):
            #if channel is stored as left+difference
            #and this is the difference, add 1 bit
            bits_per_sample = FlacReader.SAMPLE_SIZE[
                frame_header.bits_per_sample] + 1

        elif ((frame_header.channel_assignment == 9)
              and (channel_number == 0)):
            #if channel is stored as difference+right
            #and this is the difference, add 1 bit
            bits_per_sample = FlacReader.SAMPLE_SIZE[
                frame_header.bits_per_sample] + 1

        elif ((frame_header.channel_assignment == 10)
              and (channel_number == 1)):
            #if channel is stored as average+difference
            #and this is the difference, add 1 bit
            bits_per_sample = FlacReader.SAMPLE_SIZE[
                frame_header.bits_per_sample] + 1

        else:
            #otherwise, use the number from the frame header
            bits_per_sample = FlacReader.SAMPLE_SIZE[
                frame_header.bits_per_sample]

        if (subframe_header.has_wasted_bits_per_sample):
            bits_per_sample -= subframe_header.wasted_bits_per_sample

        if (subframe_header.subframe_type == 0):
            subframe = self.read_subframe_constant(block_size, bits_per_sample)

        elif (subframe_header.subframe_type == 1):
            subframe = self.read_subframe_verbatim(block_size, bits_per_sample)

        elif ((subframe_header.subframe_type & 0x38) == 0x08):
            subframe = self.read_subframe_fixed(
                subframe_header.subframe_type & 0x07, block_size,
                bits_per_sample)

        elif ((subframe_header.subframe_type & 0x20) == 0x20):
            subframe = self.read_subframe_lpc(
                (subframe_header.subframe_type & 0x1F) + 1, block_size,
                bits_per_sample)

        else:
            raise FlacStreamException('invalid subframe type')

        if (subframe_header.has_wasted_bits_per_sample):
            return array.array('i', [
                i << subframe_header.wasted_bits_per_sample for i in subframe
            ])
        else:
            return subframe

    def read_subframe_constant(self, block_size, bits_per_sample):
        sample = construct.Bits('b',
                                bits_per_sample).parse_stream(self.bitstream)

        subframe = array.array('i', [sample] * block_size)

        return subframe

    def read_subframe_verbatim(self, block_size, bits_per_sample):
        return array.array(
            'i',
            construct.StrictRepeater(
                block_size,
                construct.Bits("samples", bits_per_sample,
                               signed=True)).parse_stream(self.bitstream))

    def read_subframe_fixed(self, order, block_size, bits_per_sample):
        samples = construct.StrictRepeater(
            order,
            construct.Bits("warm_up_samples", bits_per_sample, signed=True))

        subframe = array.array('i', samples.parse_stream(self.bitstream))

        residual = self.read_residual(block_size, order)

        fixed_func = self.FIXED_FUNCTIONS[order]

        for i in xrange(len(subframe), block_size):
            fixed_func(subframe, residual, i)

        return subframe

    def read_subframe_lpc(self, order, block_size, bits_per_sample):
        samples = construct.StrictRepeater(
            order,
            construct.Bits("warm_up_samples", bits_per_sample, signed=True))

        subframe = array.array('i', samples.parse_stream(self.bitstream))

        lpc_precision = construct.Bits('lpc_precision', 4).parse_stream(
            self.bitstream) + 1

        lpc_shift = construct.Bits('lpc_shift', 5).parse_stream(self.bitstream)

        coefficients = array.array(
            'i',
            construct.StrictRepeater(
                order,
                construct.Bits('coefficients', lpc_precision,
                               signed=True)).parse_stream(self.bitstream))

        residual = self.read_residual(block_size, order)

        for i in xrange(len(subframe), block_size):
            subframe.insert(i,
                            (sum(
                [coefficients[j] * subframe[i - j - 1] for j in
                 xrange(0,len(coefficients))]) >> lpc_shift) + \
                            residual[i])

        return subframe

    def read_residual(self, block_size, predictor_order):
        rice = array.array('i')

        #add some dummy rice so that the Rice index matches
        #that of the rest of the subframe
        for i in xrange(predictor_order):
            rice.append(0)

        coding_method = self.bitstream.read(2)
        if (coding_method == '\x00\x00'):
            rice2 = False
        elif (coding_method == '\x00\x01'):
            rice2 = True
        else:
            raise FlacStreamException('invalid residual coding method')

        partition_order = construct.Bits('partition_order',
                                         4).parse_stream(self.bitstream)

        if (partition_order > 0):
            total_samples = ((block_size / 2**partition_order) -
                             predictor_order)
            rice.extend(self.read_encoded_rice(total_samples, rice2))

            for i in xrange(1, 2**partition_order):
                total_samples = (block_size / 2**partition_order)

                rice.extend(self.read_encoded_rice(total_samples, rice2))
        else:
            rice.extend(
                self.read_encoded_rice(block_size - predictor_order, rice2))

        return rice

    def read_encoded_rice(self, total_samples, rice2=False):
        bin_to_int = construct.lib.binary.bin_to_int

        samples = array.array('i')

        if (not rice2):
            rice_parameter = construct.Bits('rice_parameter',
                                            4).parse_stream(self.bitstream)
        else:
            rice_parameter = construct.Bits('rice_parameter',
                                            5).parse_stream(self.bitstream)

        if (rice_parameter != 0xF):
            #a Rice encoded residual
            for x in xrange(total_samples):

                #count the number of 0 bits before the next 1 bit
                #(unary encoding)
                #to find our most significant bits
                msb = 0
                s = self.bitstream.read(1)
                while (s != '\x01'):
                    msb += 1
                    s = self.bitstream.read(1)

                #grab the proper number of least significant bits
                lsb = bin_to_int(self.bitstream.read(rice_parameter))

                #combine msb and lsb to get the Rice-encoded value
                value = (msb << rice_parameter) | lsb
                if ((value & 0x1) == 0x1):  #negative
                    samples.append(-(value >> 1) - 1)
                else:  #positive
                    samples.append(value >> 1)
        else:
            #unencoded residual

            bits_per_sample = construct.Bits('escape_code',
                                             5).parse_stream(self.bitstream)

            sample = construct.Bits("sample", bits_per_sample, signed=True)

            for x in xrange(total_samples):
                samples.append(sample.parse_stream(self.bitstream))

        return samples
Ejemplo n.º 13
0
 def __init__(self, name):
     construct.Adapter.__init__(
         self,
         construct.RepeatUntil(lambda obj, ctx: obj == 1,
                               construct.Byte(name)))
Ejemplo n.º 14
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)
Ejemplo n.º 15
0
class BSMParser(interface.FileObjectParser):
  """Parser for BSM files."""

  NAME = 'bsm_log'
  DESCRIPTION = 'Parser for BSM log files.'

  # BSM supported version (0x0b = 11).
  AUDIT_HEADER_VERSION = 11

  # Magic Trail Header.
  BSM_TOKEN_TRAILER_MAGIC = b'b105'

  # IP Version constants.
  AU_IPv4 = 4
  AU_IPv6 = 16

  IPV4_STRUCT = construct.UBInt32('ipv4')

  IPV6_STRUCT = construct.Struct(
      'ipv6',
      construct.UBInt64('high'),
      construct.UBInt64('low'))

  # Tested structures.
  # INFO: I have ommited the ID in the structures declaration.
  #       I used the BSM_TYPE first to read the ID, and then, the structure.
  # Tokens always start with an ID value that identifies their token
  # type and subsequent structure.
  _BSM_TOKEN = construct.UBInt8('token_id')

  # Data type structures.
  BSM_TOKEN_DATA_CHAR = construct.String('value', 1)
  BSM_TOKEN_DATA_SHORT = construct.UBInt16('value')
  BSM_TOKEN_DATA_INTEGER = construct.UBInt32('value')

  # Common structure used by other structures.
  # audit_uid: integer, uid that generates the entry.
  # effective_uid: integer, the permission user used.
  # effective_gid: integer, the permission group used.
  # real_uid: integer, user id of the user that execute the process.
  # real_gid: integer, group id of the group that execute the process.
  # pid: integer, identification number of the process.
  # session_id: unknown, need research.
  BSM_TOKEN_SUBJECT_SHORT = construct.Struct(
      'subject_data',
      construct.UBInt32('audit_uid'),
      construct.UBInt32('effective_uid'),
      construct.UBInt32('effective_gid'),
      construct.UBInt32('real_uid'),
      construct.UBInt32('real_gid'),
      construct.UBInt32('pid'),
      construct.UBInt32('session_id'))

  # Common structure used by other structures.
  # Identify the kind of inet (IPv4 or IPv6)
  # TODO: instead of 16, AU_IPv6 must be used.
  BSM_IP_TYPE_SHORT = construct.Struct(
      'bsm_ip_type_short',
      construct.UBInt32('net_type'),
      construct.Switch(
          'ip_addr',
          _BSMTokenGetNetType,
          {16: IPV6_STRUCT},
          default=IPV4_STRUCT))

  # Initial fields structure used by header structures.
  # length: integer, the length of the entry, equal to trailer (doc: length).
  # version: integer, version of BSM (AUDIT_HEADER_VERSION).
  # event_type: integer, the type of event (/etc/security/audit_event).
  # modifier: integer, unknown, need research (It is always 0).
  BSM_HEADER = construct.Struct(
      'bsm_header',
      construct.UBInt32('length'),
      construct.UBInt8('version'),
      construct.UBInt16('event_type'),
      construct.UBInt16('modifier'))

  # First token of one entry.
  # timestamp: unsigned integer, number of seconds since
  #            January 1, 1970 00:00:00 UTC.
  # microseconds: unsigned integer, number of micro seconds.
  BSM_HEADER32 = construct.Struct(
      'bsm_header32',
      BSM_HEADER,
      construct.UBInt32('timestamp'),
      construct.UBInt32('microseconds'))

  BSM_HEADER64 = construct.Struct(
      'bsm_header64',
      BSM_HEADER,
      construct.UBInt64('timestamp'),
      construct.UBInt64('microseconds'))

  BSM_HEADER32_EX = construct.Struct(
      'bsm_header32_ex',
      BSM_HEADER,
      BSM_IP_TYPE_SHORT,
      construct.UBInt32('timestamp'),
      construct.UBInt32('microseconds'))

  # Token TEXT, provides extra information.
  BSM_TOKEN_TEXT = construct.Struct(
      'bsm_token_text',
      construct.UBInt16('length'),
      construct.Array(_BSMTokenGetLength, construct.UBInt8('text')))

  # Path of the executable.
  BSM_TOKEN_PATH = BSM_TOKEN_TEXT

  # Identified the end of the record (follow by TRAILER).
  # status: integer that identifies the status of the exit (BSM_ERRORS).
  # return: returned value from the operation.
  BSM_TOKEN_RETURN32 = construct.Struct(
      'bsm_token_return32',
      construct.UBInt8('status'),
      construct.UBInt32('return_value'))

  BSM_TOKEN_RETURN64 = construct.Struct(
      'bsm_token_return64',
      construct.UBInt8('status'),
      construct.UBInt64('return_value'))

  # Identified the number of bytes that was written.
  # magic: 2 bytes that identifies the TRAILER (BSM_TOKEN_TRAILER_MAGIC).
  # length: integer that has the number of bytes from the entry size.
  BSM_TOKEN_TRAILER = construct.Struct(
      'bsm_token_trailer',
      construct.UBInt16('magic'),
      construct.UBInt32('record_length'))

  # A 32-bits argument.
  # num_arg: the number of the argument.
  # name_arg: the argument's name.
  # text: the string value of the argument.
  BSM_TOKEN_ARGUMENT32 = construct.Struct(
      'bsm_token_argument32',
      construct.UBInt8('num_arg'),
      construct.UBInt32('name_arg'),
      construct.UBInt16('length'),
      construct.Array(_BSMTokenGetLength, construct.UBInt8('text')))

  # A 64-bits argument.
  # num_arg: integer, the number of the argument.
  # name_arg: text, the argument's name.
  # text: the string value of the argument.
  BSM_TOKEN_ARGUMENT64 = construct.Struct(
      'bsm_token_argument64',
      construct.UBInt8('num_arg'),
      construct.UBInt64('name_arg'),
      construct.UBInt16('length'),
      construct.Array(_BSMTokenGetLength, construct.UBInt8('text')))

  # Identify an user.
  # terminal_id: unknown, research needed.
  # terminal_addr: unknown, research needed.
  BSM_TOKEN_SUBJECT32 = construct.Struct(
      'bsm_token_subject32',
      BSM_TOKEN_SUBJECT_SHORT,
      construct.UBInt32('terminal_port'),
      IPV4_STRUCT)

  # Identify an user using a extended Token.
  # terminal_port: unknown, need research.
  # net_type: unknown, need research.
  BSM_TOKEN_SUBJECT32_EX = construct.Struct(
      'bsm_token_subject32_ex',
      BSM_TOKEN_SUBJECT_SHORT,
      construct.UBInt32('terminal_port'),
      BSM_IP_TYPE_SHORT)

  # au_to_opaque // AUT_OPAQUE
  BSM_TOKEN_OPAQUE = BSM_TOKEN_TEXT

  # au_to_seq // AUT_SEQ
  BSM_TOKEN_SEQUENCE = BSM_TOKEN_DATA_INTEGER

  # Program execution with options.
  # For each argument we are going to have a string+ "\x00".
  # Example: [00 00 00 02][41 42 43 00 42 42 00]
  #          2 Arguments, Arg1: [414243] Arg2: [4242].
  BSM_TOKEN_EXEC_ARGUMENTS = construct.UBInt32('number_arguments')

  BSM_TOKEN_EXEC_ARGUMENT = construct.Struct(
      'bsm_token_exec_argument',
      construct.RepeatUntil(
          _BSMTokenIsEndOfString, construct.StaticField("text", 1)))

  # au_to_in_addr // AUT_IN_ADDR:
  BSM_TOKEN_ADDR = IPV4_STRUCT

  # au_to_in_addr_ext // AUT_IN_ADDR_EX:
  BSM_TOKEN_ADDR_EXT = construct.Struct(
      'bsm_token_addr_ext',
      construct.UBInt32('net_type'),
      IPV6_STRUCT)

  # au_to_ip // AUT_IP:
  # TODO: parse this header in the correct way.
  BSM_TOKEN_IP = construct.String('binary_ipv4_add', 20)

  # au_to_ipc // AUT_IPC:
  BSM_TOKEN_IPC = construct.Struct(
      'bsm_token_ipc',
      construct.UBInt8('object_type'),
      construct.UBInt32('object_id'))

  # au_to_ipc_perm // au_to_ipc_perm
  BSM_TOKEN_IPC_PERM = construct.Struct(
      'bsm_token_ipc_perm',
      construct.UBInt32('user_id'),
      construct.UBInt32('group_id'),
      construct.UBInt32('creator_user_id'),
      construct.UBInt32('creator_group_id'),
      construct.UBInt32('access_mode'),
      construct.UBInt32('slot_seq'),
      construct.UBInt32('key'))

  # au_to_iport // AUT_IPORT:
  BSM_TOKEN_PORT = construct.UBInt16('port_number')

  # au_to_file // AUT_OTHER_FILE32:
  BSM_TOKEN_FILE = construct.Struct(
      'bsm_token_file',
      construct.UBInt32('timestamp'),
      construct.UBInt32('microseconds'),
      construct.UBInt16('length'),
      construct.Array(_BSMTokenGetLength, construct.UBInt8('text')))

  # au_to_subject64 // AUT_SUBJECT64:
  BSM_TOKEN_SUBJECT64 = construct.Struct(
      'bsm_token_subject64',
      BSM_TOKEN_SUBJECT_SHORT,
      construct.UBInt64('terminal_port'),
      IPV4_STRUCT)

  # au_to_subject64_ex // AU_IPv4:
  BSM_TOKEN_SUBJECT64_EX = construct.Struct(
      'bsm_token_subject64_ex',
      BSM_TOKEN_SUBJECT_SHORT,
      construct.UBInt32('terminal_port'),
      construct.UBInt32('terminal_type'),
      BSM_IP_TYPE_SHORT)

  # au_to_process32 // AUT_PROCESS32:
  BSM_TOKEN_PROCESS32 = construct.Struct(
      'bsm_token_process32',
      BSM_TOKEN_SUBJECT_SHORT,
      construct.UBInt32('terminal_port'),
      IPV4_STRUCT)

  # au_to_process64 // AUT_PROCESS32:
  BSM_TOKEN_PROCESS64 = construct.Struct(
      'bsm_token_process64',
      BSM_TOKEN_SUBJECT_SHORT,
      construct.UBInt64('terminal_port'),
      IPV4_STRUCT)

  # au_to_process32_ex // AUT_PROCESS32_EX:
  BSM_TOKEN_PROCESS32_EX = construct.Struct(
      'bsm_token_process32_ex',
      BSM_TOKEN_SUBJECT_SHORT,
      construct.UBInt32('terminal_port'),
      BSM_IP_TYPE_SHORT)

  # au_to_process64_ex // AUT_PROCESS64_EX:
  BSM_TOKEN_PROCESS64_EX = construct.Struct(
      'bsm_token_process64_ex',
      BSM_TOKEN_SUBJECT_SHORT,
      construct.UBInt64('terminal_port'),
      BSM_IP_TYPE_SHORT)

  # au_to_sock_inet32 // AUT_SOCKINET32:
  BSM_TOKEN_AUT_SOCKINET32 = construct.Struct(
      'bsm_token_aut_sockinet32',
      construct.UBInt16('net_type'),
      construct.UBInt16('port_number'),
      IPV4_STRUCT)

  # Info: checked against the source code of XNU, but not against
  #       real BSM file.
  BSM_TOKEN_AUT_SOCKINET128 = construct.Struct(
      'bsm_token_aut_sockinet128',
      construct.UBInt16('net_type'),
      construct.UBInt16('port_number'),
      IPV6_STRUCT)

  INET6_ADDR_TYPE = construct.Struct(
      'addr_type',
      construct.UBInt16('ip_type'),
      construct.UBInt16('source_port'),
      construct.UBInt64('saddr_high'),
      construct.UBInt64('saddr_low'),
      construct.UBInt16('destination_port'),
      construct.UBInt64('daddr_high'),
      construct.UBInt64('daddr_low'))

  INET4_ADDR_TYPE = construct.Struct(
      'addr_type',
      construct.UBInt16('ip_type'),
      construct.UBInt16('source_port'),
      construct.UBInt32('source_address'),
      construct.UBInt16('destination_port'),
      construct.UBInt32('destination_address'))

  # au_to_socket_ex // AUT_SOCKET_EX
  # TODO: Change the 26 for unixbsm.BSM_PROTOCOLS.INET6.
  BSM_TOKEN_AUT_SOCKINET32_EX = construct.Struct(
      'bsm_token_aut_sockinet32_ex',
      construct.UBInt16('socket_domain'),
      construct.UBInt16('socket_type'),
      construct.Switch(
          'structure_addr_port',
          _BSMTokenGetSocketDomain,
          {26: INET6_ADDR_TYPE},
          default=INET4_ADDR_TYPE))

  # au_to_sock_unix // AUT_SOCKUNIX
  BSM_TOKEN_SOCKET_UNIX = construct.Struct(
      'bsm_token_au_to_sock_unix',
      construct.UBInt16('family'),
      construct.RepeatUntil(
          _BSMTokenIsEndOfString,
          construct.StaticField("path", 1)))

  # au_to_data // au_to_data
  # how to print: bsmtoken.BSM_TOKEN_DATA_PRINT.
  # type: bsmtoken.BSM_TOKEN_DATA_TYPE.
  # unit_count: number of type values.
  # BSM_TOKEN_DATA has a end field = type * unit_count
  BSM_TOKEN_DATA = construct.Struct(
      'bsm_token_data',
      construct.UBInt8('how_to_print'),
      construct.UBInt8('data_type'),
      construct.UBInt8('unit_count'))

  # au_to_attr32 // AUT_ATTR32
  BSM_TOKEN_ATTR32 = construct.Struct(
      'bsm_token_attr32',
      construct.UBInt32('file_mode'),
      construct.UBInt32('uid'),
      construct.UBInt32('gid'),
      construct.UBInt32('file_system_id'),
      construct.UBInt64('file_system_node_id'),
      construct.UBInt32('device'))

  # au_to_attr64 // AUT_ATTR64
  BSM_TOKEN_ATTR64 = construct.Struct(
      'bsm_token_attr64',
      construct.UBInt32('file_mode'),
      construct.UBInt32('uid'),
      construct.UBInt32('gid'),
      construct.UBInt32('file_system_id'),
      construct.UBInt64('file_system_node_id'),
      construct.UBInt64('device'))

  # au_to_exit // AUT_EXIT
  BSM_TOKEN_EXIT = construct.Struct(
      'bsm_token_exit',
      construct.UBInt32('status'),
      construct.UBInt32('return_value'))

  # au_to_newgroups // AUT_NEWGROUPS
  # INFO: we must read BSM_TOKEN_DATA_INTEGER for each group.
  BSM_TOKEN_GROUPS = construct.UBInt16('group_number')

  # au_to_exec_env == au_to_exec_args
  BSM_TOKEN_EXEC_ENV = BSM_TOKEN_EXEC_ARGUMENTS

  # au_to_zonename //AUT_ZONENAME
  BSM_TOKEN_ZONENAME = BSM_TOKEN_TEXT

  # Token ID.
  # List of valid Token_ID.
  # Token_ID -> (NAME_STRUCTURE, STRUCTURE)
  # Only the checked structures are been added to the valid structures lists.
  _BSM_TOKEN_TYPES = {
      17: ('BSM_TOKEN_FILE', BSM_TOKEN_FILE),
      19: ('BSM_TOKEN_TRAILER', BSM_TOKEN_TRAILER),
      20: ('BSM_HEADER32', BSM_HEADER32),
      21: ('BSM_HEADER64', BSM_HEADER64),
      33: ('BSM_TOKEN_DATA', BSM_TOKEN_DATA),
      34: ('BSM_TOKEN_IPC', BSM_TOKEN_IPC),
      35: ('BSM_TOKEN_PATH', BSM_TOKEN_PATH),
      36: ('BSM_TOKEN_SUBJECT32', BSM_TOKEN_SUBJECT32),
      38: ('BSM_TOKEN_PROCESS32', BSM_TOKEN_PROCESS32),
      39: ('BSM_TOKEN_RETURN32', BSM_TOKEN_RETURN32),
      40: ('BSM_TOKEN_TEXT', BSM_TOKEN_TEXT),
      41: ('BSM_TOKEN_OPAQUE', BSM_TOKEN_OPAQUE),
      42: ('BSM_TOKEN_ADDR', BSM_TOKEN_ADDR),
      43: ('BSM_TOKEN_IP', BSM_TOKEN_IP),
      44: ('BSM_TOKEN_PORT', BSM_TOKEN_PORT),
      45: ('BSM_TOKEN_ARGUMENT32', BSM_TOKEN_ARGUMENT32),
      47: ('BSM_TOKEN_SEQUENCE', BSM_TOKEN_SEQUENCE),
      96: ('BSM_TOKEN_ZONENAME', BSM_TOKEN_ZONENAME),
      113: ('BSM_TOKEN_ARGUMENT64', BSM_TOKEN_ARGUMENT64),
      114: ('BSM_TOKEN_RETURN64', BSM_TOKEN_RETURN64),
      116: ('BSM_HEADER32_EX', BSM_HEADER32_EX),
      119: ('BSM_TOKEN_PROCESS64', BSM_TOKEN_PROCESS64),
      122: ('BSM_TOKEN_SUBJECT32_EX', BSM_TOKEN_SUBJECT32_EX),
      127: ('BSM_TOKEN_AUT_SOCKINET32_EX', BSM_TOKEN_AUT_SOCKINET32_EX),
      128: ('BSM_TOKEN_AUT_SOCKINET32', BSM_TOKEN_AUT_SOCKINET32)}

  # Untested structures.
  # When not tested structure is found, we try to parse using also
  # these structures.
  BSM_TYPE_LIST_NOT_TESTED = {
      49: ('BSM_TOKEN_ATTR', BSM_TOKEN_ATTR32),
      50: ('BSM_TOKEN_IPC_PERM', BSM_TOKEN_IPC_PERM),
      52: ('BSM_TOKEN_GROUPS', BSM_TOKEN_GROUPS),
      59: ('BSM_TOKEN_GROUPS', BSM_TOKEN_GROUPS),
      60: ('BSM_TOKEN_EXEC_ARGUMENTS', BSM_TOKEN_EXEC_ARGUMENTS),
      61: ('BSM_TOKEN_EXEC_ENV', BSM_TOKEN_EXEC_ENV),
      62: ('BSM_TOKEN_ATTR32', BSM_TOKEN_ATTR32),
      82: ('BSM_TOKEN_EXIT', BSM_TOKEN_EXIT),
      115: ('BSM_TOKEN_ATTR64', BSM_TOKEN_ATTR64),
      117: ('BSM_TOKEN_SUBJECT64', BSM_TOKEN_SUBJECT64),
      123: ('BSM_TOKEN_PROCESS32_EX', BSM_TOKEN_PROCESS32_EX),
      124: ('BSM_TOKEN_PROCESS64_EX', BSM_TOKEN_PROCESS64_EX),
      125: ('BSM_TOKEN_SUBJECT64_EX', BSM_TOKEN_SUBJECT64_EX),
      126: ('BSM_TOKEN_ADDR_EXT', BSM_TOKEN_ADDR_EXT),
      129: ('BSM_TOKEN_AUT_SOCKINET128', BSM_TOKEN_AUT_SOCKINET128),
      130: ('BSM_TOKEN_SOCKET_UNIX', BSM_TOKEN_SOCKET_UNIX)}

  MESSAGE_CAN_NOT_SAVE = (
      'Plaso: some tokens from this entry can not be saved. Entry at 0x{0:X} '
      'with unknown token id "0x{1:X}".')

  # BSM token types:
  # https://github.com/openbsm/openbsm/blob/master/sys/bsm/audit_record.h
  _BSM_TOKEN_TYPE_ARGUMENT32 = 45
  _BSM_TOKEN_TYPE_ARGUMENT64 = 113
  _BSM_TOKEN_TYPE_ATTR = 49
  _BSM_TOKEN_TYPE_ATTR32 = 62
  _BSM_TOKEN_TYPE_ATTR64 = 115
  _BSM_TOKEN_TYPE_EXEC_ARGUMENTS = 60
  _BSM_TOKEN_TYPE_EXEC_ENV = 61
  _BSM_TOKEN_TYPE_EXIT = 82
  _BSM_TOKEN_TYPE_HEADER32 = 20
  _BSM_TOKEN_TYPE_HEADER32_EX = 116
  _BSM_TOKEN_TYPE_HEADER64 = 21
  _BSM_TOKEN_TYPE_PATH = 35
  _BSM_TOKEN_TYPE_PROCESS32 = 38
  _BSM_TOKEN_TYPE_PROCESS32_EX = 123
  _BSM_TOKEN_TYPE_PROCESS64 = 119
  _BSM_TOKEN_TYPE_PROCESS64_EX = 124
  _BSM_TOKEN_TYPE_RETURN32 = 39
  _BSM_TOKEN_TYPE_RETURN64 = 114
  _BSM_TOKEN_TYPE_SUBJECT32 = 36
  _BSM_TOKEN_TYPE_SUBJECT32_EX = 122
  _BSM_TOKEN_TYPE_SUBJECT64 = 117
  _BSM_TOKEN_TYPE_SUBJECT64_EX = 125
  _BSM_TOKEN_TYPE_TEXT = 40
  _BSM_TOKEN_TYPE_ZONENAME = 96

  _BSM_ARGUMENT_TOKEN_TYPES = (
      _BSM_TOKEN_TYPE_ARGUMENT32,
      _BSM_TOKEN_TYPE_ARGUMENT64)

  _BSM_ATTR_TOKEN_TYPES = (
      _BSM_TOKEN_TYPE_ATTR,
      _BSM_TOKEN_TYPE_ATTR32,
      _BSM_TOKEN_TYPE_ATTR64)

  _BSM_EXEV_TOKEN_TYPES = (
      _BSM_TOKEN_TYPE_EXEC_ARGUMENTS,
      _BSM_TOKEN_TYPE_EXEC_ENV)

  _BSM_HEADER_TOKEN_TYPES = (
      _BSM_TOKEN_TYPE_HEADER32,
      _BSM_TOKEN_TYPE_HEADER32_EX,
      _BSM_TOKEN_TYPE_HEADER64)

  _BSM_PROCESS_TOKEN_TYPES = (
      _BSM_TOKEN_TYPE_PROCESS32,
      _BSM_TOKEN_TYPE_PROCESS64)

  _BSM_PROCESS_EX_TOKEN_TYPES = (
      _BSM_TOKEN_TYPE_PROCESS32_EX,
      _BSM_TOKEN_TYPE_PROCESS64_EX)

  _BSM_RETURN_TOKEN_TYPES = (
      _BSM_TOKEN_TYPE_EXIT,
      _BSM_TOKEN_TYPE_RETURN32,
      _BSM_TOKEN_TYPE_RETURN64)

  _BSM_SUBJECT_TOKEN_TYPES = (
      _BSM_TOKEN_TYPE_SUBJECT32,
      _BSM_TOKEN_TYPE_SUBJECT64)

  _BSM_SUBJECT_EX_TOKEN_TYPES = (
      _BSM_TOKEN_TYPE_SUBJECT32_EX,
      _BSM_TOKEN_TYPE_SUBJECT64_EX)

  _BSM_UTF8_BYTE_ARRAY_TOKEN_TYPES = (
      _BSM_TOKEN_TYPE_PATH,
      _BSM_TOKEN_TYPE_TEXT,
      _BSM_TOKEN_TYPE_ZONENAME)

  def __init__(self):
    """Initializes a parser object."""
    super(BSMParser, self).__init__()
    # Create the dictionary with all token IDs: tested and untested.
    self._bsm_type_list_all = self._BSM_TOKEN_TYPES.copy()
    self._bsm_type_list_all.update(self.BSM_TYPE_LIST_NOT_TESTED)

  def _CopyByteArrayToBase16String(self, byte_array):
    """Copies a byte array into a base-16 encoded Unicode string.

    Args:
      byte_array (bytes): A byte array.

    Returns:
      str: a base-16 encoded Unicode string.
    """
    return ''.join(['{0:02x}'.format(byte) for byte in byte_array])

  def _CopyUtf8ByteArrayToString(self, byte_array):
    """Copies a UTF-8 encoded byte array into a Unicode string.

    Args:
      byte_array (bytes): A byte array containing an UTF-8 encoded string.

    Returns:
      str: A Unicode string.
    """
    byte_stream = b''.join(map(chr, byte_array))

    try:
      string = byte_stream.decode('utf-8')
    except UnicodeDecodeError:
      logging.warning('Unable to decode UTF-8 formatted byte array.')
      string = byte_stream.decode('utf-8', errors='ignore')

    string, _, _ = string.partition(b'\x00')
    return string

  def _IPv4Format(self, address):
    """Formats an IPv4 address as a human readable string.

    Args:
      address (int): IPv4 address.

    Returns:
      str: human readable string of IPv4 address in 4 octet representation:
          "1.2.3.4".
    """
    ipv4_string = self.IPV4_STRUCT.build(address)
    return socket.inet_ntoa(ipv4_string)

  def _IPv6Format(self, high, low):
    """Formats an IPv6 address as a human readable string.

    Args:
      high (int): upper 64-bit part of the IPv6 address.
      low (int): lower 64-bit part of the IPv6 address.

    Returns:
      str: human readable string of IPv6 address.
    """
    ipv6_string = self.IPV6_STRUCT.build(
        construct.Container(high=high, low=low))
    # socket.inet_ntop not supported in Windows.
    if hasattr(socket, 'inet_ntop'):
      return socket.inet_ntop(socket.AF_INET6, ipv6_string)

    # TODO: this approach returns double "::", illegal IPv6 addr.
    str_address = binascii.hexlify(ipv6_string)
    address = []
    blank = False
    for pos in range(0, len(str_address), 4):
      if str_address[pos:pos + 4] == '0000':
        if not blank:
          address.append('')
          blank = True
      else:
        blank = False
        address.append(str_address[pos:pos + 4].lstrip('0'))
    return ':'.join(address)

  def _ParseBSMEvent(self, parser_mediator, file_object):
    """Parses a BSM entry (BSMEvent) from the file-like object.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      file_object (dfvfs.FileIO): a file-like object.

    Returns:
      bool: True if the BSM entry was parsed.
    """
    record_start_offset = file_object.tell()

    try:
      token_type = self._BSM_TOKEN.parse_stream(file_object)
    except (IOError, construct.FieldError) as exception:
      parser_mediator.ProduceExtractionError((
          'unable to parse BSM token type at offset: 0x{0:08x} with error: '
          '{1:s}.').format(record_start_offset, exception))
      return False

    if token_type not in self._BSM_HEADER_TOKEN_TYPES:
      parser_mediator.ProduceExtractionError(
          'unsupported token type: {0:d} at offset: 0x{1:08x}.'.format(
              token_type, record_start_offset))
      # TODO: if it is a Mac OS X, search for the trailer magic value
      #       as a end of the entry can be a possibility to continue.
      return False

    _, record_structure = self._BSM_TOKEN_TYPES.get(token_type, ('', None))

    try:
      token = record_structure.parse_stream(file_object)
    except (IOError, construct.FieldError) as exception:
      parser_mediator.ProduceExtractionError((
          'unable to parse BSM record at offset: 0x{0:08x} with error: '
          '{1:s}.').format(record_start_offset, exception))
      return False

    event_type = bsmtoken.BSM_AUDIT_EVENT.get(
        token.bsm_header.event_type, 'UNKNOWN')
    event_type = '{0:s} ({1:d})'.format(
        event_type, token.bsm_header.event_type)

    timestamp = (token.timestamp * 1000000) + token.microseconds
    date_time = dfdatetime_posix_time.PosixTimeInMicroseconds(
        timestamp=timestamp)

    record_length = token.bsm_header.length
    record_end_offset = record_start_offset + record_length

    # A dict of tokens that has the entry.
    extra_tokens = {}

    # Read until we reach the end of the record.
    while file_object.tell() < record_end_offset:
      # Check if it is a known token.
      try:
        token_type = self._BSM_TOKEN.parse_stream(file_object)
      except (IOError, construct.FieldError):
        logging.warning(
            'Unable to parse the Token ID at position: {0:d}'.format(
                file_object.tell()))
        return False

      _, record_structure = self._BSM_TOKEN_TYPES.get(token_type, ('', None))

      if not record_structure:
        pending = record_end_offset - file_object.tell()
        new_extra_tokens = self.TryWithUntestedStructures(
            file_object, token_type, pending)
        extra_tokens.update(new_extra_tokens)
      else:
        token = record_structure.parse_stream(file_object)
        new_extra_tokens = self.FormatToken(token_type, token, file_object)
        extra_tokens.update(new_extra_tokens)

    if file_object.tell() > record_end_offset:
      logging.warning(
          'Token ID {0:d} not expected at position 0x{1:08x}.'
          'Jumping for the next entry.'.format(
              token_type, file_object.tell()))
      try:
        file_object.seek(
            record_end_offset - file_object.tell(), os.SEEK_CUR)
      except (IOError, construct.FieldError) as exception:
        logging.warning(
            'Unable to jump to next entry with error: {0:s}'.format(exception))
        return False

    # BSM can be in more than one OS: BSD, Solaris and Mac OS X.
    if parser_mediator.platform != 'MacOSX':
      event_data = BSMEventData()
    else:
      event_data = MacBSMEventData()

      # In Mac OS X the last two tokens are the return status and the trailer.
      return_value = extra_tokens.get('BSM_TOKEN_RETURN32')
      if not return_value:
        return_value = extra_tokens.get('BSM_TOKEN_RETURN64')
      if not return_value:
        return_value = 'UNKNOWN'

      event_data.return_value = return_value

    event_data.event_type = event_type
    event_data.extra_tokens = extra_tokens
    event_data.offset = record_start_offset
    event_data.record_length = record_length

    # TODO: check why trailer was passed to event in original while
    # event was expecting record length.
    # if extra_tokens:
    #   trailer = extra_tokens.get('BSM_TOKEN_TRAILER', 'unknown')

    event = time_events.DateTimeValuesEvent(
        date_time, definitions.TIME_DESCRIPTION_CREATION)
    parser_mediator.ProduceEventWithEventData(event, event_data)

    return True

  def _RawToUTF8(self, byte_stream):
    """Copies a UTF-8 byte stream into a Unicode string.

    Args:
      byte_stream (bytes): byte stream containing an UTF-8 encoded string.

    Returns:
      str: A Unicode string.
    """
    try:
      string = byte_stream.decode('utf-8')
    except UnicodeDecodeError:
      logging.warning(
          'Decode UTF8 failed, the message string may be cut short.')
      string = byte_stream.decode('utf-8', errors='ignore')
    return string.partition(b'\x00')[0]

  def ParseFileObject(self, parser_mediator, file_object, **kwargs):
    """Parses a BSM file-like object.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      file_object (dfvfs.FileIO): a file-like object.

    Raises:
      UnableToParseFile: when the file cannot be parsed.
    """
    try:
      is_bsm = self.VerifyFile(parser_mediator, file_object)
    except (IOError, construct.FieldError) as exception:
      raise errors.UnableToParseFile(
          'Unable to parse BSM file with error: {0:s}'.format(exception))

    if not is_bsm:
      raise errors.UnableToParseFile('Not a BSM File, unable to parse.')

    file_object.seek(0, os.SEEK_SET)

    while self._ParseBSMEvent(parser_mediator, file_object):
      pass

  def VerifyFile(self, parser_mediator, file_object):
    """Check if the file is a BSM file.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      file_object (dfvfs.FileIO): a file-like object.

    Returns:
      bool: True if this is a valid BSM file, False otherwise.
    """
    # First part of the entry is always a Header.
    try:
      token_type = self._BSM_TOKEN.parse_stream(file_object)
    except (IOError, construct.FieldError):
      return False

    if token_type not in self._BSM_HEADER_TOKEN_TYPES:
      return False

    _, record_structure = self._BSM_TOKEN_TYPES.get(token_type, ('', None))

    try:
      header = record_structure.parse_stream(file_object)
    except (IOError, construct.FieldError):
      return False

    if header.bsm_header.version != self.AUDIT_HEADER_VERSION:
      return False

    try:
      token_identifier = self._BSM_TOKEN.parse_stream(file_object)
    except (IOError, construct.FieldError):
      return False

    # If is Mac OS X BSM file, next entry is a  text token indicating
    # if it is a normal start or it is a recovery track.
    if parser_mediator.platform == 'MacOSX':
      token_type, record_structure = self._BSM_TOKEN_TYPES.get(
          token_identifier, ('', None))

      if not record_structure:
        return False

      if token_type != 'BSM_TOKEN_TEXT':
        logging.warning('It is not a valid first entry for Mac OS X BSM.')
        return False

      try:
        token = record_structure.parse_stream(file_object)
      except (IOError, construct.FieldError):
        return

      text = self._CopyUtf8ByteArrayToString(token.text)
      if (text != 'launchctl::Audit startup' and
          text != 'launchctl::Audit recovery'):
        logging.warning('It is not a valid first entry for Mac OS X BSM.')
        return False

    return True

  def TryWithUntestedStructures(self, file_object, token_id, pending):
    """Try to parse the pending part of the entry using untested structures.

    Args:
      file_object: BSM file.
      token_id: integer with the id that comes from the unknown token.
      pending: pending length of the entry.

    Returns:
      A list of extra tokens data that can be parsed using non-tested
      structures. A message indicating that a structure cannot be parsed
      is added for unparsed structures.
    """
    # Data from the unknown structure.
    start_position = file_object.tell()
    start_token_id = token_id
    extra_tokens = {}

    # Read all the "pending" bytes.
    try:
      if token_id in self._bsm_type_list_all:
        token = self._bsm_type_list_all[token_id][1].parse_stream(file_object)
        new_extra_tokens = self.FormatToken(token_id, token, file_object)
        extra_tokens.update(new_extra_tokens)
        while file_object.tell() < (start_position + pending):
          # Check if it is a known token.
          try:
            token_id = self._BSM_TOKEN.parse_stream(file_object)
          except (IOError, construct.FieldError):
            logging.warning(
                'Unable to parse the Token ID at position: {0:d}'.format(
                    file_object.tell()))
            return
          if token_id not in self._bsm_type_list_all:
            break
          token = self._bsm_type_list_all[token_id][1].parse_stream(file_object)
          new_extra_tokens = self.FormatToken(token_id, token, file_object)
          extra_tokens.update(new_extra_tokens)
    except (IOError, construct.FieldError):
      token_id = 255

    next_entry = (start_position + pending)
    if file_object.tell() != next_entry:
      # Unknown Structure.
      logging.warning('Unknown Token at "0x{0:X}", ID: {1} (0x{2:X})'.format(
          start_position - 1, token_id, token_id))
      # TODO: another way to save this information must be found.
      extra_tokens.update(
          {'message': self.MESSAGE_CAN_NOT_SAVE.format(
              start_position - 1, start_token_id)})
      # Move to next entry.
      file_object.seek(next_entry - file_object.tell(), os.SEEK_CUR)
      # It returns null list because it doesn't know witch structure was
      # the incorrect structure that makes that it can arrive to the spected
      # end of the entry.
      return {}
    return extra_tokens

  def FormatToken(self, token_id, token, file_object):
    """Parse the Token depending of the type of the structure.

    Args:
      token_id (int): identification of the token_type.
      token (structure): token struct to parse.
      file_object: BSM file.

    Returns:
      (dict): parsed Token values.

    Keys for returned dictionary are token name like BSM_TOKEN_SUBJECT32.
    Values of this dictionary are key-value pairs like terminal_ip:127.0.0.1.
    """
    if token_id not in self._bsm_type_list_all:
      return {}

    bsm_type, _ = self._bsm_type_list_all.get(token_id, ['', ''])

    if token_id in self._BSM_UTF8_BYTE_ARRAY_TOKEN_TYPES:
      try:
        string = self._CopyUtf8ByteArrayToString(token.text)
      except TypeError:
        string = 'Unknown'
      return {bsm_type: string}

    elif token_id in self._BSM_RETURN_TOKEN_TYPES:
      return {bsm_type: {
          'error': bsmtoken.BSM_ERRORS.get(token.status, 'Unknown'),
          'token_status': token.status,
          'call_status': token.return_value
      }}

    elif token_id in self._BSM_SUBJECT_TOKEN_TYPES:
      return {bsm_type: {
          'aid': token.subject_data.audit_uid,
          'euid': token.subject_data.effective_uid,
          'egid': token.subject_data.effective_gid,
          'uid': token.subject_data.real_uid,
          'gid': token.subject_data.real_gid,
          'pid': token.subject_data.pid,
          'session_id': token.subject_data.session_id,
          'terminal_port': token.terminal_port,
          'terminal_ip': self._IPv4Format(token.ipv4)
      }}

    elif token_id in self._BSM_SUBJECT_EX_TOKEN_TYPES:
      if token.bsm_ip_type_short.net_type == self.AU_IPv6:
        ip = self._IPv6Format(
            token.bsm_ip_type_short.ip_addr.high,
            token.bsm_ip_type_short.ip_addr.low)
      elif token.bsm_ip_type_short.net_type == self.AU_IPv4:
        ip = self._IPv4Format(token.bsm_ip_type_short.ip_addr)
      else:
        ip = 'unknown'
      return {bsm_type: {
          'aid': token.subject_data.audit_uid,
          'euid': token.subject_data.effective_uid,
          'egid': token.subject_data.effective_gid,
          'uid': token.subject_data.real_uid,
          'gid': token.subject_data.real_gid,
          'pid': token.subject_data.pid,
          'session_id': token.subject_data.session_id,
          'terminal_port': token.terminal_port,
          'terminal_ip': ip
      }}

    elif token_id in self._BSM_ARGUMENT_TOKEN_TYPES:
      string = self._CopyUtf8ByteArrayToString(token.text)
      return {bsm_type: {
          'string': string,
          'num_arg': token.num_arg,
          'is': token.name_arg}}

    elif token_id in self._BSM_EXEV_TOKEN_TYPES:
      arguments = []
      for _ in range(0, token):
        sub_token = self.BSM_TOKEN_EXEC_ARGUMENT.parse_stream(file_object)
        string = self._CopyUtf8ByteArrayToString(sub_token.text)
        arguments.append(string)
      return {bsm_type: ' '.join(arguments)}

    elif bsm_type == 'BSM_TOKEN_AUT_SOCKINET32':
      return {bsm_type: {
          'protocols':
          bsmtoken.BSM_PROTOCOLS.get(token.net_type, 'UNKNOWN'),
          'net_type': token.net_type,
          'port': token.port_number,
          'address': self._IPv4Format(token.ipv4)
      }}

    elif bsm_type == 'BSM_TOKEN_AUT_SOCKINET128':
      return {bsm_type: {
          'protocols':
          bsmtoken.BSM_PROTOCOLS.get(token.net_type, 'UNKNOWN'),
          'net_type': token.net_type,
          'port': token.port_number,
          'address': self._IPv6Format(token.ipv6.high, token.ipv6.low)
      }}

    elif bsm_type == 'BSM_TOKEN_ADDR':
      return {bsm_type: self._IPv4Format(token)}

    elif bsm_type == 'BSM_TOKEN_IP':
      return {'IPv4_Header': '0x{0:s}]'.format(token.encode('hex'))}

    elif bsm_type == 'BSM_TOKEN_ADDR_EXT':
      return {bsm_type: {
          'protocols':
          bsmtoken.BSM_PROTOCOLS.get(token.net_type, 'UNKNOWN'),
          'net_type': token.net_type,
          'address': self._IPv6Format(token.ipv6.high, token.ipv6.low)
      }}

    elif bsm_type == 'BSM_TOKEN_PORT':
      return {bsm_type: token}

    elif bsm_type == 'BSM_TOKEN_TRAILER':
      return {bsm_type: token.record_length}

    elif bsm_type == 'BSM_TOKEN_FILE':
      # TODO: if this timestamp is usefull, it must be extracted as a separate
      #       event object.
      timestamp = timelib.Timestamp.FromPosixTimeWithMicrosecond(
          token.timestamp, token.microseconds)
      date_time = timelib.Timestamp.CopyToDatetime(timestamp, pytz.UTC)
      date_time_string = date_time.strftime('%Y-%m-%d %H:%M:%S')

      string = self._CopyUtf8ByteArrayToString(token.text)
      return {bsm_type: {'string': string, 'timestamp': date_time_string}}

    elif bsm_type == 'BSM_TOKEN_IPC':
      return {bsm_type: {
          'object_type': token.object_type,
          'object_id': token.object_id
      }}

    elif token_id in self._BSM_PROCESS_TOKEN_TYPES:
      return {bsm_type: {
          'aid': token.subject_data.audit_uid,
          'euid': token.subject_data.effective_uid,
          'egid': token.subject_data.effective_gid,
          'uid': token.subject_data.real_uid,
          'gid': token.subject_data.real_gid,
          'pid': token.subject_data.pid,
          'session_id': token.subject_data.session_id,
          'terminal_port': token.terminal_port,
          'terminal_ip': self._IPv4Format(token.ipv4)
      }}

    elif token_id in self._BSM_PROCESS_EX_TOKEN_TYPES:
      if token.bsm_ip_type_short.net_type == self.AU_IPv6:
        ip = self._IPv6Format(
            token.bsm_ip_type_short.ip_addr.high,
            token.bsm_ip_type_short.ip_addr.low)
      elif token.bsm_ip_type_short.net_type == self.AU_IPv4:
        ip = self._IPv4Format(token.bsm_ip_type_short.ip_addr)
      else:
        ip = 'unknown'
      return {bsm_type: {
          'aid': token.subject_data.audit_uid,
          'euid': token.subject_data.effective_uid,
          'egid': token.subject_data.effective_gid,
          'uid': token.subject_data.real_uid,
          'gid': token.subject_data.real_gid,
          'pid': token.subject_data.pid,
          'session_id': token.subject_data.session_id,
          'terminal_port': token.terminal_port,
          'terminal_ip': ip
      }}

    elif bsm_type == 'BSM_TOKEN_DATA':
      data = []
      data_type = bsmtoken.BSM_TOKEN_DATA_TYPE.get(token.data_type, '')

      if data_type == 'AUR_CHAR':
        for _ in range(token.unit_count):
          data.append(self.BSM_TOKEN_DATA_CHAR.parse_stream(file_object))

      elif data_type == 'AUR_SHORT':
        for _ in range(token.unit_count):
          data.append(self.BSM_TOKEN_DATA_SHORT.parse_stream(file_object))

      elif data_type == 'AUR_INT32':
        for _ in range(token.unit_count):
          data.append(self.BSM_TOKEN_DATA_INTEGER.parse_stream(file_object))

      else:
        data.append('Unknown type data')

      # TODO: the data when it is string ends with ".", HW a space is return
      #       after uses the UTF-8 conversion.
      return {bsm_type: {
          'format': bsmtoken.BSM_TOKEN_DATA_PRINT[token.how_to_print],
          'data':
          '{0}'.format(self._RawToUTF8(''.join(map(str, data))))
      }}

    elif token_id in self._BSM_ATTR_TOKEN_TYPES:
      return {bsm_type: {
          'mode': token.file_mode,
          'uid': token.uid,
          'gid': token.gid,
          'system_id': token.file_system_id,
          'node_id': token.file_system_node_id,
          'device': token.device}}

    elif bsm_type == 'BSM_TOKEN_GROUPS':
      arguments = []
      for _ in range(token):
        arguments.append(
            self._RawToUTF8(
                self.BSM_TOKEN_DATA_INTEGER.parse_stream(file_object)))
      return {bsm_type: ','.join(arguments)}

    elif bsm_type == 'BSM_TOKEN_AUT_SOCKINET32_EX':
      if bsmtoken.BSM_PROTOCOLS.get(token.socket_domain, '') == 'INET6':
        saddr = self._IPv6Format(
            token.structure_addr_port.saddr_high,
            token.structure_addr_port.saddr_low)
        daddr = self._IPv6Format(
            token.structure_addr_port.daddr_high,
            token.structure_addr_port.daddr_low)
      else:
        saddr = self._IPv4Format(token.structure_addr_port.source_address)
        daddr = self._IPv4Format(token.structure_addr_port.destination_address)

      return {bsm_type:{
          'from': saddr,
          'from_port': token.structure_addr_port.source_port,
          'to': daddr,
          'to_port': token.structure_addr_port.destination_port}}

    elif bsm_type == 'BSM_TOKEN_IPC_PERM':
      return {bsm_type: {
          'user_id': token.user_id,
          'group_id': token.group_id,
          'creator_user_id': token.creator_user_id,
          'creator_group_id': token.creator_group_id,
          'access': token.access_mode}}

    elif bsm_type == 'BSM_TOKEN_SOCKET_UNIX':
      string = self._CopyUtf8ByteArrayToString(token.path)
      return {bsm_type: {'family': token.family, 'path': string}}

    elif bsm_type == 'BSM_TOKEN_OPAQUE':
      string = self._CopyByteArrayToBase16String(token.text)
      return {bsm_type: string}

    elif bsm_type == 'BSM_TOKEN_SEQUENCE':
      return {bsm_type: token}
Ejemplo n.º 16
0
class MRUListExStringPlugin(BaseMRUListExPlugin):
  """Windows Registry plugin to parse a string MRUListEx."""

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

  FILTERS = frozenset([MRUListExStringRegistryKeyFilter()])

  URLS = [
      'http://forensicartifacts.com/2011/02/recentdocs/',
      'https://github.com/libyal/winreg-kb/wiki/MRU-keys']

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

  def _ParseMRUListExEntryValue(
      self, parser_mediator, registry_key, entry_index, entry_number,
      **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.

    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 value.DataIsString():
      value_string = value.GetDataAsObject()

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

      try:
        value_string = utf16_stream.decode('utf-16-le')
      except UnicodeDecodeError as exception:
        value_string = binary.HexifyBuffer(utf16_stream)
        logger.warning((
            '[{0:s}] Unable to decode UTF-16 stream: {1:s} in MRUListEx entry '
            'value: {2:d} in key: {3:s} with error: {4!s}').format(
                self.NAME, value_string, entry_number, registry_key.path,
                exception))

    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)
Ejemplo n.º 17
0
class RestorePointLogParser(interface.FileObjectParser):
  """A parser for Windows Restore Point (rp.log) files."""

  NAME = u'rplog'
  DESCRIPTION = u'Parser for Windows Restore Point (rp.log) files.'

  FILTERS = frozenset([
      interface.FileNameFileEntryFilter(u'rp.log')])

  _FILE_HEADER_STRUCT = construct.Struct(
      u'file_header',
      construct.ULInt32(u'event_type'),
      construct.ULInt32(u'restore_point_type'),
      construct.ULInt64(u'sequence_number'),
      construct.RepeatUntil(
          lambda character, ctx: character == b'\x00\x00',
          construct.Field(u'description', 2)))

  _FILE_FOOTER_STRUCT = construct.Struct(
      u'file_footer',
      construct.ULInt64(u'creation_time'))

  def _ParseFileHeader(self, file_object):
    """Parses the file header.

    Args:
      file_object: A file-like object to read data from.

    Returns:
      The file header construct object.

    Raises:
      UnableToParseFile: when the header cannot be parsed.
    """
    try:
      file_header = self._FILE_HEADER_STRUCT.parse_stream(file_object)
    except (IOError, construct.FieldError) as exception:
      raise errors.UnableToParseFile(
          u'Unable to parse file header with error: {0:s}'.format(exception))

    if not file_header:
      raise errors.UnableToParseFile(u'Unable to read file header')

    return file_header

  def _ParseFileFooter(self, file_object):
    """Parses the file footer.

    Args:
      file_object: A file-like object to read data from.

    Returns:
      The file footer construct object.

    Raises:
      UnableToParseFile: when the footer cannot be parsed.
    """
    try:
      file_footer = self._FILE_FOOTER_STRUCT.parse_stream(file_object)
    except (IOError, construct.FieldError) as exception:
      raise errors.UnableToParseFile(
          u'Unable to parse file footer with error: {0:s}'.format(exception))

    if not file_footer:
      raise errors.UnableToParseFile(u'Unable to read file footer')

    return file_footer

  def ParseFileObject(self, parser_mediator, file_object, **unused_kwargs):
    """Parses a Windows Restore Point (rp.log) log file-like object.

    Args:
      parser_mediator: A parser mediator object (instance of ParserMediator).
      file_object: A file-like object.

    Raises:
      UnableToParseFile: when the file cannot be parsed.
    """
    file_header = self._ParseFileHeader(file_object)

    try:
      # The struct includes the end-of-string character that we need
      # to strip off.
      description = b''.join(file_header.description).decode(u'utf16')[:-1]
    except UnicodeDecodeError as exception:
      description = u''
      parser_mediator.ProduceParseError((
          u'unable to decode description UTF-16 stream with error: '
          u'{0:s}').format(exception))

    file_object.seek(-8, os.SEEK_END)
    file_footer = self._ParseFileFooter(file_object)

    timestamp = file_footer.get(u'creation_time', None)
    if timestamp is None:
      parser_mediator.ProduceParseError(u'Timestamp not set.')
    else:
      event_object = RestorePointInfoEvent(
          timestamp, file_header.event_type, file_header.restore_point_type,
          file_header.sequence_number, description)
      parser_mediator.ProduceEvent(event_object)
Ejemplo n.º 18
0
class RestorePointLogParser(interface.FileObjectParser):
  """A parser for Windows Restore Point (rp.log) files."""

  NAME = u'rplog'
  DESCRIPTION = u'Parser for Windows Restore Point (rp.log) files.'

  FILTERS = frozenset([
      interface.FileNameFileEntryFilter(u'rp.log')])

  _FILE_HEADER_STRUCT = construct.Struct(
      u'file_header',
      construct.ULInt32(u'event_type'),
      construct.ULInt32(u'restore_point_type'),
      construct.ULInt64(u'sequence_number'),
      construct.RepeatUntil(
          lambda character, ctx: character == b'\x00\x00',
          construct.Field(u'description', 2)))

  _FILE_FOOTER_STRUCT = construct.Struct(
      u'file_footer',
      construct.ULInt64(u'creation_time'))

  def ParseFileObject(self, parser_mediator, file_object, **unused_kwargs):
    """Parses a Windows Restore Point (rp.log) log file-like object.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfvfs.
      file_object (dfvfs.FileIO): file-like object.
    """
    try:
      file_header_struct = self._FILE_HEADER_STRUCT.parse_stream(file_object)
    except (IOError, construct.FieldError) as exception:
      parser_mediator.ProduceExtractionError(
          u'unable to parse file header with error: {0:s}'.format(exception))
      return

    file_object.seek(-8, os.SEEK_END)

    try:
      file_footer_struct = self._FILE_FOOTER_STRUCT.parse_stream(file_object)
    except (IOError, construct.FieldError) as exception:
      parser_mediator.ProduceExtractionError(
          u'unable to parse file footer with error: {0:s}'.format(exception))
      return

    try:
      description = b''.join(file_header_struct.description)
      # The struct includes the end-of-string character that we need
      # to strip off.
      description = description.decode(u'utf16')[:-1]
    except UnicodeDecodeError as exception:
      description = u''
      parser_mediator.ProduceExtractionError((
          u'unable to decode description UTF-16 stream with error: '
          u'{0:s}').format(exception))

    if file_footer_struct.creation_time == 0:
      date_time = dfdatetime_semantic_time.SemanticTime(u'Not set')
    else:
      date_time = dfdatetime_filetime.Filetime(
          timestamp=file_footer_struct.creation_time)

    event_data = RestorePointEventData()
    event_data.description = description
    event_data.restore_point_event_type = file_header_struct.event_type
    event_data.restore_point_type = file_header_struct.restore_point_type
    event_data.sequence_number = file_header_struct.sequence_number

    event = time_events.DateTimeValuesEvent(
        date_time, eventdata.EventTimestamp.CREATION_TIME)
    parser_mediator.ProduceEventWithEventData(event, event_data)