Skip to content

Commit

Permalink
Preprocess available time zones on Windows #2673 (#2672)
Browse files Browse the repository at this point in the history
* Preprocess available time zones on Windows #241
  • Loading branch information
joachimmetz authored and Onager committed Jun 27, 2019
1 parent 8a3bbef commit 8213f1c
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 53 deletions.
48 changes: 39 additions & 9 deletions plaso/cli/pinfo_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,8 @@ def _PrintAnalysisReportCounter(
Args:
analysis_reports_counter (collections.Counter): number of analysis
reports per analysis plugin.
session_identifier (Optional[str]): session identifier.
session_identifier (Optional[str]): session identifier, formatted as
a UUID.
"""
if not analysis_reports_counter:
return
Expand Down Expand Up @@ -378,7 +379,8 @@ def _PrintEventLabelsCounter(
Args:
event_labels_counter (collections.Counter): number of event tags per
label.
session_identifier (Optional[str]): session identifier.
session_identifier (Optional[str]): session identifier, formatted as
a UUID.
"""
if not event_labels_counter:
return
Expand Down Expand Up @@ -410,7 +412,8 @@ def _PrintParsersCounter(self, parsers_counter, session_identifier=None):
Args:
parsers_counter (collections.Counter): number of events per parser or
parser plugin.
session_identifier (Optional[str]): session identifier.
session_identifier (Optional[str]): session identifier, formatted as
a UUID.
"""
if not parsers_counter:
self._output_writer.Write('No events stored.\n\n')
Expand All @@ -433,24 +436,34 @@ def _PrintParsersCounter(self, parsers_counter, session_identifier=None):

table_view.Write(self._output_writer)

def _PrintPreprocessingInformation(self, storage_reader, session_number=None):
def _PrintPreprocessingInformation(
self, storage_reader, session_identifier=None):
"""Prints the details of the preprocessing information.
Args:
storage_reader (StorageReader): storage reader.
session_number (Optional[int]): session number.
session_identifier (Optional[str]): session identifier, formatted as
a UUID.
"""
knowledge_base_object = knowledge_base.KnowledgeBase()

storage_reader.ReadPreprocessingInformation(knowledge_base_object)

# TODO: replace session_number by session_identifier.
lookup_identifier = session_identifier
if lookup_identifier:
# The knowledge base requires the session identifier to be formatted in
# hexadecimal representation.
lookup_identifier = lookup_identifier.replace('-', '')

system_configuration = knowledge_base_object.GetSystemConfigurationArtifact(
session_identifier=session_number)
session_identifier=lookup_identifier)
if not system_configuration:
return

title = 'System configuration'
if session_identifier:
title = '{0:s}: {1:s}'.format(title, session_identifier)

table_view = views.ViewsFactory.GetTableView(
self._views_format_type, title=title)

Expand All @@ -477,7 +490,23 @@ def _PrintPreprocessingInformation(self, storage_reader, session_number=None):

table_view.Write(self._output_writer)

title = 'Available time zones'
if session_identifier:
title = '{0:s}: {1:s}'.format(title, session_identifier)

table_view = views.ViewsFactory.GetTableView(
self._views_format_type,
column_names=['Name', ''], title=title)

for time_zone in system_configuration.available_time_zones:
table_view.AddRow([time_zone.name, ''])

table_view.Write(self._output_writer)

title = 'User accounts'
if session_identifier:
title = '{0:s}: {1:s}'.format(title, session_identifier)

table_view = views.ViewsFactory.GetTableView(
self._views_format_type,
column_names=['Username', 'User directory'], title=title)
Expand All @@ -494,7 +523,7 @@ def _PrintSessionsDetails(self, storage_reader):
Args:
storage_reader (BaseStore): storage.
"""
for session_number, session in enumerate(storage_reader.GetSessions()):
for session in storage_reader.GetSessions():
session_identifier = uuid.UUID(hex=session.identifier)
session_identifier = '{0!s}'.format(session_identifier)

Expand Down Expand Up @@ -545,7 +574,8 @@ def _PrintSessionsDetails(self, storage_reader):
table_view.Write(self._output_writer)

if self._verbose:
self._PrintPreprocessingInformation(storage_reader, session_number + 1)
self._PrintPreprocessingInformation(
storage_reader, session_identifier=session_identifier)

self._PrintParsersCounter(
session.parsers_counter, session_identifier=session_identifier)
Expand Down
31 changes: 25 additions & 6 deletions plaso/containers/artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ class HostnameArtifact(ArtifactAttributeContainer):
Also see:
https://en.wikipedia.org/wiki/Hostname
http://cybox.mitre.org/language/version2.1/xsddocs/objects/
Hostname_Object.html
Cybox / Stix Hostname Object
Attributes:
name (str): name of the host according to the naming schema.
Expand Down Expand Up @@ -226,6 +225,7 @@ class SystemConfigurationArtifact(ArtifactAttributeContainer):
system installation e.g. Windows or Linux.
Attributes:
available_time_zones (list[TimeZone]): available time zones.
code_page (str): system code page.
hostname (HostnameArtifact): hostname.
keyboard_layout (str): keyboard layout.
Expand All @@ -247,6 +247,7 @@ def __init__(self, code_page=None, time_zone=None):
time_zone (Optional[str]): system time zone.
"""
super(SystemConfigurationArtifact, self).__init__()
self.available_time_zones = []
self.code_page = code_page
self.hostname = None
self.keyboard_layout = None
Expand All @@ -257,12 +258,30 @@ def __init__(self, code_page=None, time_zone=None):
self.user_accounts = []


class TimeZoneArtifact(ArtifactAttributeContainer):
"""Time zone artifact attribute container.
Attributes:
name (str): name describing the time zone e.g. Greenwich Standard Time.
"""
CONTAINER_TYPE = 'time_zone'

def __init__(self, name=None):
"""Initializes a time zone artifact.
Args:
name (Optional[str]): name describing the time zone e.g. Greenwich
Standard Time.
"""
super(TimeZoneArtifact, self).__init__()
self.name = name


class UserAccountArtifact(ArtifactAttributeContainer):
"""User account artifact attribute container.
Also see:
http://cybox.mitre.org/language/version2.1/xsddocs/objects/
User_Account_Object.html
Cybox / Stix User Account Object
Attributes:
full_name (str): name describing the user e.g. full name.
Expand All @@ -276,7 +295,7 @@ class UserAccountArtifact(ArtifactAttributeContainer):
def __init__(
self, full_name=None, group_identifier=None, identifier=None,
path_separator='/', user_directory=None, username=None):
"""Initializes an user artifact.
"""Initializes a user account artifact.
Args:
full_name (Optional[str]): name describing the user e.g. full name.
Expand Down Expand Up @@ -311,4 +330,4 @@ def GetUserDirectoryPathSegments(self):

manager.AttributeContainersManager.RegisterAttributeContainers([
EnvironmentVariableArtifact, HostnameArtifact, SystemConfigurationArtifact,
UserAccountArtifact])
TimeZoneArtifact, UserAccountArtifact])
39 changes: 38 additions & 1 deletion plaso/engine/knowledge_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,19 @@ class KnowledgeBase(object):
def __init__(self):
"""Initializes a knowledge base."""
super(KnowledgeBase, self).__init__()
self._available_time_zones = {}
self._codepage = 'cp1252'
self._environment_variables = {}
self._hostnames = {}
self._time_zone = pytz.UTC
self._user_accounts = {}
self._values = {}

@property
def available_time_zones(self):
"""list[TimeZone]: available time zones of the current session."""
return self._available_time_zones.get(self.CURRENT_SESSION, {}).values()

@property
def codepage(self):
"""str: codepage of the current session."""
Expand Down Expand Up @@ -63,6 +69,26 @@ def year(self):
"""int: year of the current session."""
return self.GetValue('year', default_value=0)

def AddAvailableTimeZone(self, time_zone, session_identifier=CURRENT_SESSION):
"""Adds an available time zone.
Args:
time_zone (TimeZoneArtifact): time zone artifact.
session_identifier (Optional[str])): session identifier, where
CURRENT_SESSION represents the active session.
Raises:
KeyError: if the time zone already exists.
"""
if session_identifier not in self._available_time_zones:
self._available_time_zones[session_identifier] = {}

available_time_zones = self._available_time_zones[session_identifier]
if time_zone.name in available_time_zones:
raise KeyError('Time zone: {0:s} already exists.'.format(time_zone.name))

available_time_zones[time_zone.name] = time_zone

def AddUserAccount(self, user_account, session_identifier=CURRENT_SESSION):
"""Adds an user account.
Expand Down Expand Up @@ -187,6 +213,13 @@ def GetSystemConfigurationArtifact(self, session_identifier=CURRENT_SESSION):

system_configuration.time_zone = time_zone

available_time_zones = self._available_time_zones.get(
session_identifier, {})
# In Python 3 dict.values() returns a type dict_values, which will cause
# the JSON serializer to raise a TypeError.
system_configuration.available_time_zones = list(
available_time_zones.values())

user_accounts = self._user_accounts.get(session_identifier, {})
# In Python 3 dict.values() returns a type dict_values, which will cause
# the JSON serializer to raise a TypeError.
Expand Down Expand Up @@ -306,8 +339,12 @@ def ReadSystemConfigurationArtifact(
'Unsupported time zone: {0:s}, defaulting to {1:s}'.format(
system_configuration.time_zone, self.timezone.zone))

self._available_time_zones[session_identifier] = {
time_zone.name: time_zone
for time_zone in system_configuration.available_time_zones}

self._user_accounts[session_identifier] = {
user_account.username: user_account
user_account.identifier: user_account
for user_account in system_configuration.user_accounts}

def SetCodepage(self, codepage):
Expand Down
27 changes: 27 additions & 0 deletions plaso/preprocessors/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,32 @@ def Collect(self, knowledge_base):
pass


class WindowsAvailableTimeZonesPlugin(
interface.WindowsRegistryKeyArtifactPreprocessorPlugin):
"""The Windows available time zones plugin."""

ARTIFACT_DEFINITION_NAME = 'WindowsAvailableTimeZones'

def _ParseKey(self, knowledge_base, registry_key, value_name):
"""Parses a Windows Registry key for a preprocessing attribute.
Args:
knowledge_base (KnowledgeBase): to fill with preprocessing information.
registry_key (dfwinreg.WinRegistryKey): Windows Registry key.
value_name (str): name of the Windows Registry value.
Raises:
errors.PreProcessFail: if the preprocessing fails.
"""
time_zone_artifact = artifacts.TimeZoneArtifact(name=registry_key.name)

try:
knowledge_base.AddAvailableTimeZone(time_zone_artifact)
except KeyError:
# TODO: add and store preprocessing errors.
pass


class WindowsCodepagePlugin(
interface.WindowsRegistryValueArtifactPreprocessorPlugin):
"""The Windows codepage plugin."""
Expand Down Expand Up @@ -479,6 +505,7 @@ class WindowsWinDirEnvironmentVariablePlugin(
WindowsAllUsersAppDataKnowledgeBasePlugin,
WindowsAllUsersProfileEnvironmentVariablePlugin,
WindowsAllUsersAppProfileKnowledgeBasePlugin,
WindowsAvailableTimeZonesPlugin,
WindowsCodepagePlugin, WindowsHostnamePlugin,
WindowsProgramDataEnvironmentVariablePlugin,
WindowsProgramDataKnowledgeBasePlugin,
Expand Down
5 changes: 3 additions & 2 deletions plaso/storage/sqlite/sqlite_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -1085,9 +1085,10 @@ def ReadPreprocessingInformation(self, knowledge_base):
generator = self._GetAttributeContainers(
self._CONTAINER_TYPE_SYSTEM_CONFIGURATION)
for stream_number, system_configuration in enumerate(generator):
# TODO: replace stream_number by session_identifier.
session_start = self._GetAttributeContainerByIndex(
self._CONTAINER_TYPE_SESSION_START, stream_number)
knowledge_base.ReadSystemConfigurationArtifact(
system_configuration, session_identifier=stream_number)
system_configuration, session_identifier=session_start.identifier)

def WritePreprocessingInformation(self, knowledge_base):
"""Writes preprocessing information.
Expand Down
Loading

0 comments on commit 8213f1c

Please sign in to comment.