mirror of
https://github.com/elisspace/autopsy.git
synced 2026-10-02 07:19:53 +00:00
Added comments and support for group calls in line
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user