Added comments and support for group calls in line

This commit is contained in:
U-BASIS\dsmyda
2019-09-23 18:06:31 -04:00
parent 6d2672eda3
commit 31726ad882
3 changed files with 158 additions and 18 deletions
+96 -17
View File
@@ -54,7 +54,48 @@ import general
class LineAnalyzer(general.AndroidComponentAnalyzer):
"""
Parses the Line App databases for TSK contacts & message artifacts.
Parses the Line App databases for contacts,
message and call log artifacts.
About Line parser for v9.15.1:
- Line Database Design Details:
Line has unique ids associated with their users and with their groups. These ids
are referred to as mid in the database.
Databases:
- naver_line: contains contact and msg artifacts
- call_history: contains call artifacts
Tables:
- naver_line/groups: This table contains group ids paired with metadata
about the group (such as creator, group name, etc).
- naver_line/membership This table maps user mids to group ids. Each record
contains 1 group id and 1 user mid.
- naver_line/chat_history This table contains all chat history for private
(1 to 1) and group conversations. It maps a user mid
or group id to the message details. The user mid and
group id are stored into the same column "chat_id".
If the message direction is incoming, the sender mid
is stored in the from_mid column.
- naver_line/contacts This table contains all Line contacts known to the
device.
- call_history/call_history This table contains all call history for private
and group calls. It maps a user mid or a group id
to the call details. The user mid and group id are
stored in the "caller_mid" column.
- Implementation Details:
1) Both group calls and single calls are extracted in one query. The general approach
is to build one result table with both contact mids and group ids.
This result is consistently labeled contact_list_with_groups queries below.
This table is then joined once onto the messages table to produce all communication
data.
2) Both group chats and single chats are extracted in one query.
"""
def __init__(self):
@@ -195,22 +236,38 @@ class LineCallLogsParser(TskCallLogsParser):
def __init__(self, calllog_db):
super(LineCallLogsParser, self).__init__(calllog_db.runQuery(
"""
SELECT substr(CallH.call_type, -1) AS direction,
CallH.start_time AS start_time,
CallH.end_time AS end_time,
ConT.server_name AS name,
CallH.voip_type AS call_type,
ConT.m_id
FROM call_history AS CallH
JOIN naver.contacts AS ConT
ON CallH.caller_mid = ConT.m_id
SELECT Substr(CH.call_type, -1) AS direction,
CH.start_time AS start_time,
CH.end_time AS end_time,
contacts_list_with_groups.members AS group_members,
contacts_list_with_groups.member_names AS names,
CH.caller_mid,
CH.voip_type AS call_type,
CH.voip_gc_media_type AS group_call_type
FROM (SELECT id,
Group_concat(M.m_id) AS members,
Group_concat(Replace(C.server_name, ",", "")) AS member_names
FROM membership AS M
JOIN naver.contacts AS C
ON M.m_id = C.m_id
GROUP BY id
UNION
SELECT m_id,
NULL,
server_name
FROM naver.contacts) AS contacts_list_with_groups
JOIN call_history AS CH
ON CH.caller_mid = contacts_list_with_groups.id
"""
)
)
)
self._OUTGOING_CALL_TYPE = "O"
self._INCOMING_CALL_TYPE = "I"
self._VIDEO_CALL_TYPE = "V"
self._AUDIO_CALL_TYPE = "A"
self._GROUP_CALL_TYPE = "G"
self._GROUP_VIDEO_CALL_TYPE = "VIDEO"
self._GROUP_AUDIO_CALL_TYPE = "AUDIO"
def get_call_direction(self):
direction = self.result_set.getString("direction")
@@ -232,21 +289,40 @@ class LineCallLogsParser(TskCallLogsParser):
def get_phone_number_to(self):
if self.get_call_direction() == self.OUTGOING_CALL:
return Account.Address(self.result_set.getString("m_id"),
self.result_set.getString("name"))
group_members = self.result_set.getString("group_members")
if group_members is not None:
group_members = group_members.split(",")
group_names = self.result_set.getString("names").split(",")
recipients = []
for member_id, member_name in zip(group_members, group_names):
recipients.append(Account.Address(member_id, member_name))
return recipients
return Account.Address(self.result_set.getString("caller_mid"),
self.result_set.getString("names"))
return super(LineCallLogsParser, self).get_phone_number_to()
def get_phone_number_from(self):
if self.get_call_direction() == self.INCOMING_CALL:
return Account.Address(self.result_set.getString("m_id"),
self.result_set.getString("name"))
return Account.Address(self.result_set.getString("caller_mid"),
self.result_set.getString("names"))
return super(LineCallLogsParser, self).get_phone_number_from()
def get_call_type(self):
if self.result_set.getString("call_type") == self._VIDEO_CALL_TYPE:
call_type = self.result_set.getString("call_type")
if call_type == self._VIDEO_CALL_TYPE:
return self.VIDEO_CALL
if self.result_set.getString("call_type") == self._AUDIO_CALL_TYPE:
if call_type == self._AUDIO_CALL_TYPE:
return self.AUDIO_CALL
if call_type == self._GROUP_CALL_TYPE:
g_type = self.result_set.getString("group_call_type")
if g_type == self._GROUP_VIDEO_CALL_TYPE:
return self.VIDEO_CALL
if g_type == self._GROUP_AUDIO_CALL_TYPE:
return self.AUDIO_CALL
return super(LineCallLogsParser, self).get_call_type()
class LineContactsParser(TskContactsParser):
@@ -279,6 +355,9 @@ class LineMessagesParser(TskMessagesParser):
"""
def __init__(self, message_db):
"""
"""
super(LineMessagesParser, self).__init__(message_db.runQuery(
"""
SELECT contact_list_with_groups.name,
+1 -1
View File
@@ -61,7 +61,7 @@ class SkypeAnalyzer(general.AndroidComponentAnalyzer):
About version 8.15.0.428 (9/17/2019) Skype database:
- There are 4 tables this parser uses:
1) person - this table appears to hold all contacts known to the user.
2) user - this table holds information pertaining to the user.
2) user - this table holds information about the user.
3) particiapnt - Yes, that is not a typo. This table maps group chat
ids to skype ids (1 to many).
4) chatItem - This table contains all messages. It maps the group id or
+61
View File
@@ -56,6 +56,67 @@ class WhatsAppAnalyzer(general.AndroidComponentAnalyzer):
"""
Parses the WhatsApp databases for TSK contact, message
and calllog artifacts.
About WhatsApp parser for v2.19.244:
- Database Design Details:
There are 2 databases and 6 tables this parser uses.
1) Prerequisties:
Each user is assigned a whatsapp id, refered to as jid in the
database. A jid is of the form:
####...####@whatsapp.net
where # is a placeholder for an arbitrary length of digits 1-9.
2) Databases:
- databases/msgstore.db: contains msg and call log info
- databases/wa.db: contains contact info
3) Tables:
- wa/wa_contacts: Each record maps a jid to a users personal
details, such as name and phone number.
- msgstore/call_log: Each call made on the device is a single row
in the call_log table. Each record holds
information such as duration, direction, and
type (Video or Audio).
- msgstore/call_log_participant_v2: Each row of this table maps a jid to
a call_log record. Multiple rows that
share a call_log id indicate a group call.
- msgstore/messages: Each message is represented as a single row.
A row maps a jid or a gjid (group jid) to some
message details. Both the jid and gjid are
stored in 1 column, called key_remote_jid.
gjid's are of the form:
#####...###-#####...####@g.us
where # is a place holder for a digit 1-9. The
'-' is a fixed character surrounded by digits
of arbiturary length n and m.
If the message is not from a group, the jid the
message is to/from is stored in key_remote_jid
column. If it is a group, the key_remote_jid
column contains the gjid and the 'from' jid is
stored in a secondary column called
remote_resource.
- msgstore/group_participants: Each row of this table maps a jid to a gjid.
- msgstore/jid: This table stores raw jid string. Some tables
only store the jid_row. A join must be
performed to get the jid value out.
- Implementation details:
1) Group calls and single calls are extracted in two different queries.
2) Group messages and single messages are extracted in 1 query.
- The general approach was to build one complete contacts table containing
both jid and gjid. A join can be performed once on all of the messages.
All jids that are part of a gjid were concatenated into a comma seperated
list of jids.
"""
def __init__(self):