Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented Apple biome file parser #4878

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions plaso/data/formatters/ios.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Plaso iOS related event formatters.
---
type: 'conditional'
data_type: 'apple:biome:app_launch'
message:
- 'Launcher: {launcher}'
- 'Launched Application: {launched_application}'
short_message:
- 'Launched Application: {launched_application}'
short_source: 'LOG'
source: 'Apple biome application launch'
---
type: 'conditional'
data_type: 'ios:app_privacy:access'
message:
- 'Accessor Identifier: {accessor_identifier}'
Expand Down
4 changes: 4 additions & 0 deletions plaso/data/presets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ parsers:
name: ios
description: Preset for iOS.
parsers:
- biome/application_install_biome
- biome/application_launch_biome
- jsonl/ios_application_privacy
- plist/ios_identityservices
- sqlite/imessage
Expand Down Expand Up @@ -70,6 +72,8 @@ operating_systems:
- {family: MacOS}
parsers:
- asl_log
- biome/application_install_biome
- biome/application_launch_biome
- bencode
- bsm_log
- cups_ipp
Expand Down
24 changes: 24 additions & 0 deletions plaso/data/timeliner.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,30 @@ attribute_mappings:
description: 'Recorded Time'
place_holder_event: true
---
data_type: 'apple:biome:app_launch'
attribute_mappings:
- name: 'event_time'
description: 'Event Time'
- name: 'launcher'
description: 'process that launched the application'
- name: 'launched application'
description: 'name of the launched application'
place_holder_event: true
---
data_type: 'apple:biome:app_linstall'
attribute_mappings:
- name: 'event_time'
description: 'Event Time'
- name: 'action_guid'
description: 'GUID for the action of installing the application'
- name: 'action_name'
description: 'name of the action'
- name: 'application_name'
description: 'name of the application'
- name: 'bundle_identifier'
description: 'bundle identifier of the application'
place_holder_event: true
---
data_type: 'av:defender:detection_history'
attribute_mappings:
- name: 'recorded_time'
Expand Down
2 changes: 2 additions & 0 deletions plaso/parsers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""This file imports Python modules that register parsers."""

from plaso.parsers import android_app_usage
from plaso.parsers import apple_biome
from plaso.parsers import asl
from plaso.parsers import bencode_parser
from plaso.parsers import bodyfile
Expand Down Expand Up @@ -53,6 +54,7 @@
from plaso.parsers import winrestore

# Register parser plugins.
from plaso.parsers import biome_plugins
from plaso.parsers import bencode_plugins
from plaso.parsers import czip_plugins
from plaso.parsers import esedb_plugins
Expand Down
180 changes: 180 additions & 0 deletions plaso/parsers/apple_biome.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
"""A parser for Apple biome files, aka SEGB files."""
import os

from plaso.lib import dtfabric_helper
from plaso.lib import errors
from plaso.lib import specification
from plaso.parsers import interface
from plaso.parsers import manager


class AppleBiomeFile(dtfabric_helper.DtFabricHelper):
"""Apple biome (aka SEGB) file.

Attributes:
header (segb_header): Header of the file.
records (list[segb_record]): All the records recovered from the file.
version (str): file version number.
"""

_DEFINITION_FILE = os.path.join(
os.path.dirname(__file__), 'apple_biome.yaml')

def __init__(self):
"""Initializes an Apple biome file."""
super(AppleBiomeFile, self).__init__()
self.header = None
self.records = []
self.version = None

def _ReadAllRecords(self, file_object, starting_offset):
"""Iterates over all the records in the Apple biome file.

Args:
file_object (dfvfs.FileIO): file-like object.
starting_offset (int): offset from which to start reading records.
"""
data_type_map = self._GetDataTypeMap(self.version)
file_size = file_object.get_size()
file_offset = starting_offset

while file_offset < file_size:
record, record_size = self._ReadStructureFromFileObject(
file_object, file_offset, data_type_map)

file_offset += record_size

# Padding
_, alignment = divmod(file_offset, 8)
if alignment > 0:
alignment = 8 - alignment

file_offset += alignment

# Case where the record has a blank header and no content
# record_size includes the record header, record.size only counts content
# This signals the end of records.
if record_size == 32 and record.size == 0:
break

# Case where the record has a valid header but the content is all nulls.
# These can be at the top of the file.
if set(record.protobuf) == {0}:
continue

self.records.append(record)

def _ReadFileHeader(self, file_object):
"""Determines the version of the Apple biome file and returns its header.

Args:
file_object (dfvfs.FileIO): file-like object.

Returns:
File header and data size of the header.
"""
data_type_map = self._GetDataTypeMap('segb_header_v1')

header, header_size = self._ReadStructureFromFileObject(
file_object, 0, data_type_map)

if header.segb_magic == b'SEGB':
return header, header_size

return None, 0

def Open(self, file_object):
"""Opens an Apple biome file.

Args:
file_object (dfvfs.FileIO): file-like object.

Raises:
ValueError: if the file object is missing.
errors.WrongParser: if the segb_record version is not recognized.
"""
if not file_object:
raise ValueError('Missing file_object.')

self.header, header_size = self._ReadFileHeader(file_object)

if header_size == 56:
self.version = 'segb_record_v1'
else:
raise errors.WrongParser('File could not be parsed.')

self._ReadAllRecords(file_object, header_size)


class AppleBiomeParser(interface.FileObjectParser):
"""Parses Apple biome file-like objects."""

NAME = 'biome'
DATA_FORMAT = 'Apple biome'

_plugin_classes = {}

def __int__(self):
"""Initializes a parser."""
super(AppleBiomeParser, self).__init__()

@classmethod
def GetFormatSpecification(cls):
"""Retrieves the format specification.

Returns:
FormatSpecification: format specification.
"""
format_specification = specification.FormatSpecification(cls.NAME)
format_specification.AddNewSignature(b'SEGB', offset=52)
return format_specification

def ParseFileObject(self, parser_mediator, file_object):
"""Parses an Apple biome files.

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

Raises:
WrongParser: when the file cannot be parsed.
"""
biome_file = AppleBiomeFile()
biome_file.Open(file_object)

for plugin_name, plugin in self._plugins_per_name.items():
if parser_mediator.abort:
break

profiling_name = '/'.join([self.NAME, plugin.NAME])
parser_mediator.SampleFormatCheckStartTiming(profiling_name)

try:
result = False
# Some of the records may have missing fields, but most will conform.
for record in biome_file.records:
result = plugin.CheckRequiredSchema(record.protobuf)
if result:
break
finally:
parser_mediator.SampleFormatCheckStopTiming(profiling_name)

if not result:
continue

parser_mediator.SampleStartTiming(profiling_name)

try:
plugin.UpdateChainAndProcess(parser_mediator, biome_file=biome_file)

except Exception as exception: # pylint: disable=broad-except
parser_mediator.ProduceExtractionWarning((
'plugin: {0:s} unable to parse Apple biome file with error: '
'{1!s}').format(plugin_name, exception))

finally:
parser_mediator.SampleStopTiming(profiling_name)


manager.ParsersManager.RegisterParser(AppleBiomeParser)
79 changes: 79 additions & 0 deletions plaso/parsers/apple_biome.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# dtFabric format specification.
---
name: apple_biome
type: format
description: Apple biome file (aka SEGB file)
---
name: byte
type: integer
attributes:
format: unsigned
size: 1
units: bytes
---
name: uint32
type: integer
attributes:
format: unsigned
size: 4
units: bytes
---
name: float64
aliases: [double, DOUBLE]
type: floating-point
description: 64-bit double precision floating-point type
attributes:
size: 8
units: bytes
---
name: segb_header_v1
type: structure
attributes:
byte_order: little-endian
members:
- name: unknown0
data_type: uint32
- name: unknown1
type: stream
element_data_type: byte
number_of_elements: 4
- name: unknown2
type: stream
element_data_type: byte
number_of_elements: 8
- name: unknown3
data_type: uint32
- name: filename
type: stream
element_data_type: byte
number_of_elements: 16
- name: unknown4
type: stream
element_data_type: byte
number_of_elements: 16
- name: segb_magic
type: stream
element_data_type: byte
number_of_elements: 4
---
name: segb_record_v1
type: structure
attributes:
byte_order: little-endian
members:
- name: size
data_type: uint32
- name: unknown1
data_type: uint32
- name: timestamp1
data_type: float64
- name: timestamp2
data_type: float64
- name: unknown2
data_type: uint32
- name: unknown3
data_type: uint32
- name: protobuf
type: stream
element_data_type: byte
number_of_elements: segb_record_v1.size
5 changes: 5 additions & 0 deletions plaso/parsers/biome_plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
"""Imports for the apple biome parser plugins."""

from plaso.parsers.biome_plugins import app_launch
from plaso.parsers.biome_plugins import app_install
Loading