Skip to content
New issue

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

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

Already on GitHub? # to your account

SQLite parser for iOS Notes file #4944

Open
wants to merge 6 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
Expand Up @@ -140,6 +140,16 @@ short_source: 'HISTORY'
source: 'iOS netusage.sqlite database'
---
type: 'conditional'
data_type: 'ios:notes:note'
message:
- 'Title: {title}'
- 'Content: {snippet}'
short_message:
- 'Title: {title}'
short_source: 'SQLITE'
source: 'iOS notes database'
---
type: 'conditional'
data_type: 'ios:powerlog:application_usage'
message:
- 'Application: {bundle_identifier}'
Expand Down
8 changes: 8 additions & 0 deletions plaso/data/timeliner.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,14 @@ attribute_mappings:
description: 'Start Time'
place_holder_event: true
---
data_type: 'ios:notes:note'
attribute_mappings:
- name: 'creation_time'
description: 'Creation Time'
- name: 'modification_time'
description: 'Modification Time'
place_holder_event: true
---
data_type: 'ios:powerlog:application_usage'
attribute_mappings:
- name: 'start_time'
Expand Down
1 change: 1 addition & 0 deletions plaso/parsers/sqlite_plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from plaso.parsers.sqlite_plugins import ios_datausage
from plaso.parsers.sqlite_plugins import ios_kik
from plaso.parsers.sqlite_plugins import ios_netusage
from plaso.parsers.sqlite_plugins import ios_notes
from plaso.parsers.sqlite_plugins import ios_powerlog
from plaso.parsers.sqlite_plugins import ios_screentime
from plaso.parsers.sqlite_plugins import ios_twitter
Expand Down
190 changes: 190 additions & 0 deletions plaso/parsers/sqlite_plugins/ios_notes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# -*- coding: utf-8 -*-
"""SQLite parser plugin for iOS Notes database files."""

from dfdatetime import cocoa_time as dfdatetime_cocoa_time

from plaso.containers import events
from plaso.parsers import sqlite
from plaso.parsers.sqlite_plugins import interface


class IOSNotesEventData(events.EventData):
"""iOS Notes event data.

Attributes:
creation_time (dfdatetime.DateTimeValues): date and time the note was
created.
modification_time (dfdatetime.DateTimeValues): date and time the note was
last modified.
snippet (str): snippet of the note.
title (str): title of the note.
"""

DATA_TYPE = 'ios:notes:note'

def __init__(self):
"""Initializes event data."""
super(IOSNotesEventData, self).__init__(data_type=self.DATA_TYPE)
self.creation_time = None
self.modification_time = None
self.snippet = None
self.title = None


class IOSNotesPlugin(interface.SQLitePlugin):
"""SQLite parser plugin for iOS Notes database files."""

NAME = 'ios_notes'
DATA_FORMAT = 'iOS Notes SQLite database file'

REQUIRED_STRUCTURE = {
'ZICCLOUDSYNCINGOBJECT': frozenset([
'ZCREATIONDATE3', 'ZMODIFICATIONDATE1', 'ZTITLE1', 'ZSNIPPET'])}

QUERIES = [
('SELECT ZCREATIONDATE3, ZMODIFICATIONDATE1, ZTITLE1,ZSNIPPET'
'from ZICCLOUDSYNCINGOBJECT',
'ParseNoteRow')]

SCHEMAS = [{
'ZICCLOUDSYNCINGOBJECT': (
'CREATE TABLE ZICCLOUDSYNCINGOBJECT ( Z_PK INTEGER PRIMARY KEY,'
'Z_ENT INTEGER, Z_OPT INTEGER, ZCRYPTOITERATIONCOUNT INTEGER,'
'ZISPASSWORDPROTECTED INTEGER, ZMARKEDFORDELETION INTEGER,'
'ZMINIMUMSUPPORTEDNOTESVERSION INTEGER, ZNEEDSINITIALFETCHFROMCLOUD'
'INTEGER, ZNEEDSTOBEFETCHEDFROMCLOUD INTEGER,'
'ZNEEDSTOSAVEUSERSPECIFICRECORD INTEGER, ZCLOUDSTATE INTEGER,'
'ZACCOUNT INTEGER, ZCHECKEDFORLOCATION INTEGER, ZFILESIZE INTEGER,'
'ZHANDWRITINGSUMMARYVERSION INTEGER, ZHASMARKUPDATA INTEGER,'
'ZIMAGECLASSIFICATIONSUMMARYVERSION INTEGER,'
'ZIMAGEFILTERTYPE INTEGER,'
'ZNEEDSINITIALRELATIONSHIPSETUP INTEGER, ZOCRSUMMARYVERSION INTEGER,'
'ZORIENTATION INTEGER, ZSECTION INTEGER, ZURLEXPIRED INTEGER,'
'ZLOCATION INTEGER, ZMEDIA INTEGER, ZNOTE INTEGER,'
'ZNOTEUSINGTITLEFORNOTETITLE INTEGER, ZPARENTATTACHMENT INTEGER,'
'ZAPPEARANCETYPE INTEGER, ZSCALEWHENDRAWING INTEGER,'
'ZVERSION INTEGER,'
'ZVERSIONOUTOFDATE INTEGER, ZATTACHMENT INTEGER, ZSTATE INTEGER,'
'ZACCOUNT1 INTEGER, ZACCOUNT2 INTEGER,'
'ZMENTIONNOTIFICATIONATTEMPTCOUNT'
'INTEGER, ZMENTIONNOTIFICATIONSTATE INTEGER, ZNOTE1 INTEGER,'
'ZPARENTATTACHMENT1 INTEGER, ZTYPE INTEGER, ZACCOUNT3 INTEGER,'
'ZATTACHMENT1 INTEGER, ZATTACHMENTVIEWTYPE INTEGER,'
'ZISPINNED INTEGER,'
'ZISSYSTEMPAPER INTEGER, ZLEGACYNOTEWASPLAINTEXT INTEGER,'
'ZNOTEHASCHANGES INTEGER, ZPAPERSTYLETYPE INTEGER,'
'ZPREFERREDBACKGROUNDTYPE INTEGER, ZACCOUNT4 INTEGER,'
'ZFOLDER INTEGER,'
'ZNOTEDATA INTEGER, ZTITLESOURCEATTACHMENT INTEGER,'
'ZISHIDDENNOTECONTAINER INTEGER, ZSORTORDER INTEGER, ZOWNER INTEGER,'
'ZACCOUNTTYPE INTEGER, ZDIDCHOOSETOMIGRATE INTEGER,'
'ZDIDFINISHMIGRATION'
'INTEGER, ZDIDMIGRATEONMAC INTEGER, ZSERVERSIDEUPDATETASKFAILURECOUNT'
'INTEGER, ZSTOREDATASEPARATELY INTEGER, ZACCOUNTDATA INTEGER,'
'ZCUSTOMNOTESORTTYPEVALUE INTEGER, ZFOLDERTYPE INTEGER,'
'ZIMPORTEDFROMLEGACY INTEGER, ZACCOUNT5 INTEGER, ZPARENT INTEGER,'
'ZCREATIONDATE TIMESTAMP, ZCROPPINGQUADBOTTOMLEFTX FLOAT,'
'ZCROPPINGQUADBOTTOMLEFTY FLOAT, ZCROPPINGQUADBOTTOMRIGHTX FLOAT,'
'ZCROPPINGQUADBOTTOMRIGHTY FLOAT, ZCROPPINGQUADTOPLEFTX FLOAT,'
'ZCROPPINGQUADTOPLEFTY FLOAT, ZCROPPINGQUADTOPRIGHTX FLOAT,'
'ZCROPPINGQUADTOPRIGHTY FLOAT, ZDURATION FLOAT, ZMODIFICATIONDATE'
'TIMESTAMP, ZORIGINX FLOAT, ZORIGINY FLOAT,'
'ZPREVIEWUPDATEDATE TIMESTAMP,'
'ZSIZEHEIGHT FLOAT, ZSIZEWIDTH FLOAT, ZHEIGHT FLOAT, ZMODIFIEDDATE'
'TIMESTAMP, ZSCALE FLOAT, ZWIDTH FLOAT,'
'ZSTATEMODIFICATIONDATE TIMESTAMP,'
'ZCREATIONDATE1 TIMESTAMP, ZCREATIONDATE2 TIMESTAMP,'
'ZMODIFICATIONDATEATIMPORT TIMESTAMP, ZCREATIONDATE3 TIMESTAMP,'
'ZFOLDERMODIFICATIONDATE TIMESTAMP,'
'ZLASTACTIVITYRECENTUPDATESVIEWEDDATE'
'TIMESTAMP, ZLASTACTIVITYSUMMARYVIEWEDDATE TIMESTAMP,'
'ZLASTATTRIBUTIONSVIEWEDDATE TIMESTAMP, ZLASTNOTIFIEDDATE TIMESTAMP,'
'ZLASTOPENEDDATE TIMESTAMP, ZLASTVIEWEDMODIFICATIONDATE TIMESTAMP,'
'ZLEGACYMODIFICATIONDATEATIMPORT TIMESTAMP,'
'ZMODIFICATIONDATE1 TIMESTAMP,'
'ZCUSTOMNOTESORTTYPEMODIFICATIONDATE TIMESTAMP,'
'ZDATEFORLASTTITLEMODIFICATION TIMESTAMP,'
'ZPARENTMODIFICATIONDATE TIMESTAMP,'
'ZIDENTIFIER VARCHAR, ZPASSWORDHINT VARCHAR, ZZONEOWNERNAME VARCHAR,'
'ZADDITIONALINDEXABLETEXT VARCHAR, ZFALLBACKSUBTITLEIOS VARCHAR,'
'ZFALLBACKSUBTITLEMAC VARCHAR, ZFALLBACKTITLE VARCHAR,'
'ZHANDWRITINGSUMMARY VARCHAR, ZIMAGECLASSIFICATIONSUMMARY VARCHAR,'
'ZOCRSUMMARY VARCHAR, ZREMOTEFILEURLSTRING VARCHAR, ZSUMMARY VARCHAR,'
'ZTITLE VARCHAR, ZTYPEUTI VARCHAR, ZURLSTRING VARCHAR,'
'ZUSERTITLE VARCHAR,'
'ZDEVICEIDENTIFIER VARCHAR, ZDISPLAYTEXT VARCHAR,'
'ZSTANDARDIZEDCONTENT VARCHAR,'
'ZALTTEXT VARCHAR, ZTOKENCONTENTIDENTIFIER VARCHAR,'
'ZTYPEUTI1 VARCHAR,'
'ZCONTENTHASHATIMPORT VARCHAR, ZFILENAME VARCHAR,'
'ZLEGACYCONTENTHASHATIMPORT'
'VARCHAR, ZLEGACYIMPORTDEVICEIDENTIFIER VARCHAR,'
'ZLEGACYMANAGEDOBJECTIDURIREPRESENTATION VARCHAR,'
'ZSELECTEDINKCOLORSTRING VARCHAR, ZSELECTEDINKIDENTIFIER VARCHAR,'
'ZSNIPPET VARCHAR, ZTHUMBNAILATTACHMENTIDENTIFIER VARCHAR,'
'ZTITLE1 VARCHAR, ZACCOUNTNAMEFORACCOUNTLISTSORTING VARCHAR,'
'ZNESTEDTITLEFORSORTING VARCHAR, ZNAME VARCHAR,'
'ZSERVERSIDEUPDATETASKLASTATTEMPTEDBUILD VARCHAR,'
'ZSERVERSIDEUPDATETASKLASTATTEMPTEDVERSION VARCHAR,'
'ZSERVERSIDEUPDATETASKLASTCOMPLETEDBUILD VARCHAR,'
'ZSERVERSIDEUPDATETASKLASTCOMPLETEDVERSION VARCHAR,'
'ZUSERRECORDNAME VARCHAR, ZSMARTFOLDERQUERYJSON VARCHAR,'
'ZTITLE2 VARCHAR, ZPAPERASSETSURL VARCHAR, ZPAPERDATABASEURL VARCHAR,'
'ZREPLICAIDTOBUNDLEIDENTIFIER BLOB, ZACTIVITYEVENTSDATA BLOB,'
'ZASSETCRYPTOINITIALIZATIONVECTOR BLOB, ZASSETCRYPTOTAG BLOB,'
'ZCRYPTOINITIALIZATIONVECTOR BLOB, ZCRYPTOSALT BLOB, ZCRYPTOTAG BLOB,'
'ZCRYPTOWRAPPEDKEY BLOB, ZENCRYPTEDVALUESJSON BLOB,'
'ZREPLICAIDTONOTESVERSIONDATA BLOB, ZSERVERRECORDDATA BLOB,'
'ZSERVERSHAREDATA BLOB, ZUNAPPLIEDENCRYPTEDRECORD BLOB,'
'ZUSERSPECIFICSERVERRECORDDATA BLOB, ZMERGEABLEDATA BLOB,'
'ZFALLBACKIMAGECRYPTOINITIALIZATIONVECTOR BLOB,'
'ZFALLBACKIMAGECRYPTOTAG BLOB,'
'ZLINKPRESENTATIONARCHIVEDMETADATA BLOB,'
'ZMARKUPMODELDATA BLOB, ZMERGEABLEDATA1 BLOB, ZMETADATADATA BLOB,'
'ZSYNAPSEDATA BLOB, ZCRYPTOMETADATAINITIALIZATIONVECTOR BLOB,'
'ZCRYPTOMETADATATAG BLOB, ZENCRYPTEDMETADATA BLOB, ZMETADATA BLOB,'
'ZLASTNOTIFIEDTIMESTAMPDATA BLOB, ZLASTVIEWEDTIMESTAMPDATA BLOB,'
'ZREPLICAIDTOUSERIDDICTDATA BLOB, ZCRYPTOVERIFIER BLOB,'
'ZSERVERSIDEUPDATETASKCONTINUATIONTOKEN BLOB,'
'ZMERGEABLEDATA2 BLOB )')}]

def _GetDateTimeRowValue(self, query_hash, row, value_name):
"""Retrieves a date and time value from the row.

Args:
query_hash (int): hash of the query, that uniquely
identifies the query that produced the row.
row (sqlite3.Row): row.
value_name (str): name of the value.

Returns:
dfdatetime.CocoaTime: date and time value or None if not available.
"""
timestamp = self._GetRowValue(query_hash, row, value_name)
if timestamp is None:
return None

return dfdatetime_cocoa_time.CocoaTime(timestamp=timestamp)

def ParseNoteRow(self, parser_mediator, query, row, **unused_kwargs):
"""Parses a note row.

Args:
parser_mediator (ParserMediator): parser mediator.
query (str): query.
row (sqlite3.Row): row.
"""
query_hash = hash(query)

event_data = IOSNotesEventData()
event_data.creation_time = self._GetDateTimeRowValue(
query_hash, row, 'ZCREATIONDATE3')
event_data.modification_time = self._GetDateTimeRowValue(
query_hash, row, 'ZMODIFICATIONDATE1')
event_data.title = self._GetRowValue(query_hash, row, 'ZTITLE1')
event_data.snippet = self._GetRowValue(query_hash, row, 'ZSNIPPET')

parser_mediator.ProduceEventData(event_data)


sqlite.SQLiteParser.RegisterPlugin(IOSNotesPlugin)
Binary file added test_data/NoteStore.sqlite
Binary file not shown.
43 changes: 43 additions & 0 deletions tests/parsers/sqlite_plugins/ios_notes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
"""Tests for the iOS Notes database plugin."""

import unittest

from plaso.parsers.sqlite_plugins import ios_notes

from tests.parsers.sqlite_plugins import test_lib


class IOSNotesTest(test_lib.SQLitePluginTestCase):
"""Tests for the iOS Notes database plugin."""

def testProcess(self):
"""Test the Process function."""
plugin = ios_notes.IOSNotesPlugin()
storage_writer = self._ParseDatabaseFileWithPlugin(
['NotesStore.sqlite'], plugin)

number_of_event_data = storage_writer.GetNumberOfAttributeContainers(
'event_data')
self.assertEqual(number_of_event_data, 28)

number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
'extraction_warning')
self.assertEqual(number_of_warnings, 0)

number_of_warnings = storage_writer.GetNumberOfAttributeContainers(
'recovery_warning')
self.assertEqual(number_of_warnings, 0)

expected_event_values = {
'creation_time': '2023-05-10T00:56:26.719389',
'modification_time': '2023-05-10T00:57:01.178374',
'title': 'iOS 15 Note',
'snippet': 'Here is the test iOS 15 note.'}

event_data = storage_writer.GetAttributeContainerByIndex('event_data', 25)
self.CheckEventData(event_data, expected_event_values)


if __name__ == '__main__':
unittest.main()