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

Add SQLite parser for Android Viber Call (viber_data) file #4934

Open
wants to merge 5 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
27 changes: 27 additions & 0 deletions plaso/data/formatters/android.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,33 @@ short_source: 'Twitter Android'
source: 'Twitter Android Status'
---
type: 'conditional'
data_type: 'android:viber:call'
enumeration_helpers:
- input_attribute: 'type'
output_attribute: 'type'
default_value: 'UNKNOWN'
values:
1: 'INCOMING'
2: 'OUTGOING'
- input_attribute: 'viber_call_type'
output_attribute: 'viber_call_type'
default_value: 'UNKNOWN'
values:
1: 'AUDIO CALL'
4: 'VIDEO CALL'
message:
- 'Number: {number}'
- 'Call type: {type}'
- 'Viber call type: {viber_call_type}'
- 'Duration: {duration} seconds'
short_message:
- '{number}'
- '{type}'
- '{viber_call_type}'
short_source: 'Android Viber'
source: 'Android Viber Call History'
---
type: 'conditional'
data_type: 'android:webview:cookie'
message:
- 'Host: {host}'
Expand Down
8 changes: 8 additions & 0 deletions plaso/data/timeliner.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ attribute_mappings:
description: 'Creation Time'
place_holder_event: true
---
data_type: 'android:viber:call'
attribute_mappings:
- name: 'end_time'
description: 'End Time'
- name: 'start_time'
description: 'Start Time'
place_holder_event: true
---
data_type: 'android:webview:cookie'
attribute_mappings:
- name: 'expiration_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 @@ -8,6 +8,7 @@
from plaso.parsers.sqlite_plugins import android_tango
from plaso.parsers.sqlite_plugins import android_turbo
from plaso.parsers.sqlite_plugins import android_twitter
from plaso.parsers.sqlite_plugins import android_viber_call
from plaso.parsers.sqlite_plugins import android_webview
from plaso.parsers.sqlite_plugins import android_webviewcache
from plaso.parsers.sqlite_plugins import chrome_autofill
Expand Down
140 changes: 140 additions & 0 deletions plaso/parsers/sqlite_plugins/android_viber_call.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# -*- coding: utf-8 -*-
"""SQLite parser plugin for Android Viber call history database files."""

from dfdatetime import java_time as dfdatetime_java_time

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


class AndroidViberCallEventData(events.EventData):
"""Android Viber Call event data.

Attributes:
number (str): phone number.
type (int): type of call, such as: Incoming, or Outgoing.
viber_call_type (int): type of call in Viber,(Audio Call, or Video Call).
duration (int): number of seconds the call lasted.
start_time (dfdatetime.DateTimeValues): date and time the call was started.
end_time (dfdatetime.DateTimeValues): date and time the call was stopped.
"""

DATA_TYPE = 'android:viber:call'

def __init__(self):
"""Initializes event data."""
super(AndroidViberCallEventData, self).__init__(data_type=self.DATA_TYPE)
self.number = None
self.type = None
self.viber_call_type = None
self.duration = None
self.start_time = None
self.end_time = None


class AndroidViberCallPlugin(interface.SQLitePlugin):
"""SQLite parser plugin for Android Viber call history database files.

The Android Viber call history database file is typically stored in:
viber_data
"""

NAME = 'android_viber_call'
DATA_FORMAT = 'Android Viber call history SQLite database (viber_data) file'

REQUIRED_STRUCTURE = {
'calls': frozenset(['_id', 'date', 'number', 'duration', 'type',
'viber_call_type'])}

QUERIES = [
('SELECT _id AS id, date, number, duration, type, '
'viber_call_type FROM calls', 'ParseViberCallsRow')]

SCHEMAS = [{
'blockednumbers': (
'CREATE TABLE blockednumbers (_id INTEGER PRIMARY KEY AUTOINCREMENT, '
'canonized_number TEXT NOT NULL, blocked_date LONG DEFAULT 0, '
'block_reason INTEGER DEFAULT 0, status INTEGER DEFAULT 0, '
'UNIQUE (canonized_number) ON CONFLICT REPLACE )'),
'calls': (
'CREATE TABLE calls (_id INTEGER PRIMARY KEY AUTOINCREMENT, '
'call_id LONG NOT NULL, aggregate_hash LONG NOT NULL, '
'number TEXT NOT NULL, canonized_number TEXT NOT NULL, '
'viber_call BOOLEAN DEFAULT TRUE, viber_call_type INTEGER DEFAULT 1, '
'date LONG NOT NULL, duration LONG NOT NULL, type INT NOT NULL, '
'end_reason INT DEFAULT 0, start_reason INT DEFAULT 0, '
'token LONG DEFAULT 0, looked BOOLEAN DEFAULT TRUE, '
'conference_info TEXT, group_id LONG DEFAULT 0, '
'flags INTEGER DEFAULT 0)'),
'phonebookcontact': (
'CREATE TABLE phonebookcontact (_id INTEGER PRIMARY KEY NOT NULL, '
'native_id INTEGER DEFAULT 0, display_name TEXT, phonetic_name TEXT, '
'phone_label TEXT, low_display_name TEXT, starred BOOLEAN, '
'viber BOOLEAN, contact_lookup_key TEXT, contact_hash LONG, '
'version INTEGER DEFAULT 0, has_number BOOLEAN, has_name BOOLEAN, '
'native_photo_id LONG DEFAULT 0, recently_joined_date LONG DEFAULT 0, '
'joined_date LONG DEFAULT 0, numbers_name TEXT DEFAULT '', '
'deleted BOOLEAN, flags INTEGER DEFAULT 0, '
'UNIQUE (_id) ON CONFLICT REPLACE)'),
'sqlite_sequence': (
'CREATE TABLE sqlite_sequence(name,seq)'),
'sync_data': (
'CREATE TABLE sync_data (_id INTEGER PRIMARY KEY AUTOINCREMENT, '
'canonized_phone_number TEXT NOT NULL, display_name TEXT DEFAULT '', '
'phonetic_name TEXT DEFAULT '',operation INTEGER DEFAULT 0)'),
'vibernumbers': (
'CREATE TABLE vibernumbers (_id INTEGER PRIMARY KEY AUTOINCREMENT, '
'canonized_number TEXT NOT NULL, photo TEXT DEFAULT '', '
'viber_name TEXT, clear BOOLEAN, member_id TEXT NOT NULL, '
'encrypted_member_id TEXT NOT NULL, viber_id TEXT, '
'date_of_birth TEXT)'),
'viberpay_data': (
'CREATE TABLE viberpay_data (_id INTEGER PRIMARY KEY AUTOINCREMENT, '
'encrypted_member_id TEXT NULL, canonized_phone_number TEXT NULL, '
'phone_number TEXT NULL, country_code TEXT NULL, '
'is_country_supported BOOLEAN NOT NULL, '
'default_currency_code TEXT NULL, is_viberpay_user BOOLEAN NOT NULL, '
'last_sync_date INTEGER NOT NULL)'),
'walletlist': (
'CREATE TABLE walletlist (_id INTEGER PRIMARY KEY AUTOINCREMENT, '
'country_codes INTEGER NOT NULL)'),
'walletnumbers': (
'CREATE TABLE walletnumbers (_id INTEGER PRIMARY KEY AUTOINCREMENT, '
'canonized_number TEXT NOT NULL, wallet_wu_status INTEGER DEFAULT 0, '
'UNIQUE (canonized_number) ON CONFLICT REPLACE )'),
}]

def ParseViberCallsRow(self, parser_mediator, query, row, **unused_kwargs):
"""Parses a Viber Call record row.

Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
query (str): query that created the row.
row (sqlite3.Row): row.
"""
query_hash = hash(query)

duration = self._GetRowValue(query_hash, row, 'duration')
timestamp = self._GetRowValue(query_hash, row, 'date')

event_data = AndroidViberCallEventData()
event_data.type = self._GetRowValue(query_hash, row, 'type')
event_data.duration = duration
event_data.number = self._GetRowValue(query_hash, row, 'number')
event_data.viber_call_type = (
self._GetRowValue(query_hash, row, 'viber_call_type'))
event_data.start_time = dfdatetime_java_time.JavaTime(timestamp=timestamp)

if duration:
# The duration is in seconds and the date value in milliseconds.
timestamp += duration * definitions.MILLISECONDS_PER_SECOND

event_data.end_time = dfdatetime_java_time.JavaTime(timestamp=timestamp)

parser_mediator.ProduceEventData(event_data)


sqlite.SQLiteParser.RegisterPlugin(AndroidViberCallPlugin)
Binary file added test_data/viber_data
Binary file not shown.
45 changes: 45 additions & 0 deletions tests/parsers/sqlite_plugins/android_viber_call.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Tests for the Android Viber call history plugin."""

import unittest

from tests.parsers.sqlite_plugins import test_lib
from plaso.parsers.sqlite_plugins import android_viber_call


class AndroidViberCallSQLitePluginTest(test_lib.SQLitePluginTestCase):
"""Tests for the Android Viber Call History database plugin."""

def testProcess(self):
"""Test the Process function on an Android viber_data file."""
plugin = android_viber_call.AndroidViberCallPlugin()
storage_writer = self._ParseDatabaseFileWithPlugin(['viber_data'], plugin)

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

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 = {
'type': 2,
'data_type': 'android:viber:call',
'duration': 105,
'number': '+19198887386',
'start_time': '2022-11-25T20:43:08.267+00:00',
'end_time': '2022-11-25T20:44:53.267+00:00',
'viber_call_type': 4}

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


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