mirror of
https://github.com/mvt-project/mvt.git
synced 2024-06-28 07:09:00 +00:00
379 lines
10 KiB
Python
379 lines
10 KiB
Python
# Mobile Verification Toolkit (MVT)
|
|
# Copyright (c) 2021-2022 Claudio Guarnieri.
|
|
# Use of this software is governed by the MVT License 1.1 that can be found at
|
|
# https://license.mvt.re/1.1/
|
|
|
|
import re
|
|
from datetime import datetime
|
|
|
|
from mvt.common.utils import convert_datetime_to_iso
|
|
|
|
|
|
def parse_dumpsys_accessibility(output: str) -> list:
|
|
results = []
|
|
|
|
in_services = False
|
|
for line in output.splitlines():
|
|
if line.strip().startswith("installed services:"):
|
|
in_services = True
|
|
continue
|
|
|
|
if not in_services:
|
|
continue
|
|
|
|
if line.strip() == "}":
|
|
break
|
|
|
|
service = line.split(":")[1].strip()
|
|
|
|
results.append({
|
|
"package_name": service.split("/")[0],
|
|
"service": service,
|
|
})
|
|
|
|
return results
|
|
|
|
|
|
def parse_dumpsys_activity_resolver_table(output: str) -> dict:
|
|
results = {}
|
|
|
|
in_activity_resolver_table = False
|
|
in_non_data_actions = False
|
|
intent = None
|
|
for line in output.splitlines():
|
|
if line.startswith("Activity Resolver Table:"):
|
|
in_activity_resolver_table = True
|
|
continue
|
|
|
|
if not in_activity_resolver_table:
|
|
continue
|
|
|
|
if line.startswith(" Non-Data Actions:"):
|
|
in_non_data_actions = True
|
|
continue
|
|
|
|
if not in_non_data_actions:
|
|
continue
|
|
|
|
# If we hit an empty line, the Non-Data Actions section should be
|
|
# finished.
|
|
if line.strip() == "":
|
|
break
|
|
|
|
# We detect the action name.
|
|
if (line.startswith(" " * 6) and not line.startswith(" " * 8)
|
|
and ":" in line):
|
|
intent = line.strip().replace(":", "")
|
|
results[intent] = []
|
|
continue
|
|
|
|
# If we are not in an intent block yet, skip.
|
|
if not intent:
|
|
continue
|
|
|
|
# If we are in a block but the line does not start with 8 spaces
|
|
# it means the block ended a new one started, so we reset and
|
|
# continue.
|
|
if not line.startswith(" " * 8):
|
|
intent = None
|
|
continue
|
|
|
|
# If we got this far, we are processing receivers for the
|
|
# activities we are interested in.
|
|
activity = line.strip().split(" ")[1]
|
|
package_name = activity.split("/")[0]
|
|
|
|
results[intent].append({
|
|
"package_name": package_name,
|
|
"activity": activity,
|
|
})
|
|
|
|
return results
|
|
|
|
|
|
def parse_dumpsys_battery_daily(output: str) -> list:
|
|
results = []
|
|
daily = None
|
|
daily_updates = []
|
|
for line in output.splitlines():
|
|
if line.startswith(" Daily from "):
|
|
if len(daily_updates) > 0:
|
|
results.extend(daily_updates)
|
|
daily_updates = []
|
|
|
|
timeframe = line[13:].strip()
|
|
date_from, date_to = timeframe.strip(":").split(" to ", 1)
|
|
daily = {"from": date_from[0:10], "to": date_to[0:10]}
|
|
continue
|
|
|
|
if not daily:
|
|
continue
|
|
|
|
if not line.strip().startswith("Update "):
|
|
continue
|
|
|
|
line = line.strip().replace("Update ", "")
|
|
package_name, vers = line.split(" ", 1)
|
|
vers_nr = vers.split("=", 1)[1]
|
|
|
|
already_seen = False
|
|
for update in daily_updates:
|
|
if (package_name == update["package_name"]
|
|
and vers_nr == update["vers"]):
|
|
already_seen = True
|
|
break
|
|
|
|
if not already_seen:
|
|
daily_updates.append({
|
|
"action": "update",
|
|
"from": daily["from"],
|
|
"to": daily["to"],
|
|
"package_name": package_name,
|
|
"vers": vers_nr,
|
|
})
|
|
|
|
if len(daily_updates) > 0:
|
|
results.extend(daily_updates)
|
|
|
|
return results
|
|
|
|
|
|
def parse_dumpsys_battery_history(output: str) -> list:
|
|
results = []
|
|
|
|
for line in output.splitlines():
|
|
if line.startswith("Battery History "):
|
|
continue
|
|
|
|
if line.strip() == "":
|
|
break
|
|
|
|
time_elapsed = line.strip().split(" ", 1)[0]
|
|
|
|
event = ""
|
|
if line.find("+job") > 0:
|
|
event = "start_job"
|
|
uid = line[line.find("+job")+5:line.find(":")]
|
|
service = line[line.find(":")+1:].strip('"')
|
|
package_name = service.split("/")[0]
|
|
elif line.find("-job") > 0:
|
|
event = "end_job"
|
|
uid = line[line.find("-job")+5:line.find(":")]
|
|
service = line[line.find(":")+1:].strip('"')
|
|
package_name = service.split("/")[0]
|
|
elif line.find("+running +wake_lock=") > 0:
|
|
uid = line[line.find("+running +wake_lock=")+21:line.find(":")]
|
|
event = "wake"
|
|
service = line[line.find("*walarm*:")+9:].split(" ")[0].strip('"').strip()
|
|
if service == "" or "/" not in service:
|
|
continue
|
|
|
|
package_name = service.split("/")[0]
|
|
else:
|
|
continue
|
|
|
|
results.append({
|
|
"time_elapsed": time_elapsed,
|
|
"event": event,
|
|
"uid": uid,
|
|
"package_name": package_name,
|
|
"service": service,
|
|
})
|
|
|
|
return results
|
|
|
|
|
|
def parse_dumpsys_dbinfo(output: str) -> list:
|
|
results = []
|
|
|
|
rxp = re.compile(r'.*\[([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3})\].*\[Pid:\((\d+)\)\](\w+).*sql\=\"(.+?)\"')
|
|
rxp_no_pid = re.compile(r'.*\[([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3})\][ ]{1}(\w+).*sql\=\"(.+?)\"')
|
|
|
|
pool = None
|
|
in_operations = False
|
|
for line in output.splitlines():
|
|
if line.startswith("Connection pool for "):
|
|
pool = line.replace("Connection pool for ", "").rstrip(":")
|
|
|
|
if not pool:
|
|
continue
|
|
|
|
if line.strip() == "Most recently executed operations:":
|
|
in_operations = True
|
|
continue
|
|
|
|
if not in_operations:
|
|
continue
|
|
|
|
if not line.startswith(" "):
|
|
in_operations = False
|
|
pool = None
|
|
continue
|
|
|
|
matches = rxp.findall(line)
|
|
if not matches:
|
|
matches = rxp_no_pid.findall(line)
|
|
if not matches:
|
|
continue
|
|
|
|
match = matches[0]
|
|
results.append({
|
|
"isodate": match[0],
|
|
"action": match[1],
|
|
"sql": match[2],
|
|
"path": pool,
|
|
})
|
|
else:
|
|
match = matches[0]
|
|
results.append({
|
|
"isodate": match[0],
|
|
"pid": match[1],
|
|
"action": match[2],
|
|
"sql": match[3],
|
|
"path": pool,
|
|
})
|
|
|
|
return results
|
|
|
|
|
|
def parse_dumpsys_receiver_resolver_table(output: str) -> dict:
|
|
results = {}
|
|
|
|
in_receiver_resolver_table = False
|
|
in_non_data_actions = False
|
|
intent = None
|
|
for line in output.splitlines():
|
|
if line.startswith("Receiver Resolver Table:"):
|
|
in_receiver_resolver_table = True
|
|
continue
|
|
|
|
if not in_receiver_resolver_table:
|
|
continue
|
|
|
|
if line.startswith(" Non-Data Actions:"):
|
|
in_non_data_actions = True
|
|
continue
|
|
|
|
if not in_non_data_actions:
|
|
continue
|
|
|
|
# If we hit an empty line, the Non-Data Actions section should be
|
|
# finished.
|
|
if line.strip() == "":
|
|
break
|
|
|
|
# We detect the action name.
|
|
if (line.startswith(" " * 6) and not line.startswith(" " * 8)
|
|
and ":" in line):
|
|
intent = line.strip().replace(":", "")
|
|
results[intent] = []
|
|
continue
|
|
|
|
# If we are not in an intent block yet, skip.
|
|
if not intent:
|
|
continue
|
|
|
|
# If we are in a block but the line does not start with 8 spaces
|
|
# it means the block ended a new one started, so we reset and
|
|
# continue.
|
|
if not line.startswith(" " * 8):
|
|
intent = None
|
|
continue
|
|
|
|
# If we got this far, we are processing receivers for the
|
|
# activities we are interested in.
|
|
receiver = line.strip().split(" ")[1]
|
|
package_name = receiver.split("/")[0]
|
|
|
|
results[intent].append({
|
|
"package_name": package_name,
|
|
"receiver": receiver,
|
|
})
|
|
|
|
return results
|
|
|
|
|
|
def parse_dumpsys_appops(output: str) -> list:
|
|
results = []
|
|
perm = {}
|
|
package = {}
|
|
entry = {}
|
|
uid = None
|
|
in_packages = False
|
|
|
|
for line in output.splitlines():
|
|
if line.startswith(" Uid 0:"):
|
|
in_packages = True
|
|
|
|
if not in_packages:
|
|
continue
|
|
|
|
if line.startswith(" Uid "):
|
|
uid = line[6:-1]
|
|
continue
|
|
|
|
if line.startswith(" Package "):
|
|
if entry:
|
|
perm["entries"].append(entry)
|
|
entry = {}
|
|
|
|
if package:
|
|
if perm:
|
|
package["permissions"].append(perm)
|
|
|
|
perm = {}
|
|
results.append(package)
|
|
|
|
package = {
|
|
"package_name": line[12:-1],
|
|
"permissions": [],
|
|
"uid": uid,
|
|
}
|
|
continue
|
|
|
|
if line.startswith(" ") and line[6] != " ":
|
|
if entry:
|
|
perm["entries"].append(entry)
|
|
entry = {}
|
|
if perm:
|
|
package["permissions"].append(perm)
|
|
perm = {}
|
|
|
|
perm["name"] = line.split()[0]
|
|
perm["entries"] = []
|
|
if len(line.split()) > 1:
|
|
perm["access"] = line.split()[1][1:-2]
|
|
|
|
continue
|
|
|
|
if line.startswith(" "):
|
|
# Permission entry like:
|
|
# Reject: [fg-s]2021-05-19 22:02:52.054 (-314d1h25m2s33ms)
|
|
if entry:
|
|
perm["entries"].append(entry)
|
|
entry = {}
|
|
|
|
entry["access"] = line.split(":")[0].strip()
|
|
entry["type"] = line[line.find("[")+1:line.find("]")]
|
|
|
|
try:
|
|
entry["timestamp"] = convert_datetime_to_iso(
|
|
datetime.strptime(
|
|
line[line.find("]")+1:line.find("(")].strip(),
|
|
"%Y-%m-%d %H:%M:%S.%f"))
|
|
except ValueError:
|
|
# Invalid date format
|
|
pass
|
|
|
|
if line.strip() == "":
|
|
break
|
|
|
|
if entry:
|
|
perm["entries"].append(entry)
|
|
if perm:
|
|
package["permissions"].append(perm)
|
|
if package:
|
|
results.append(package)
|
|
|
|
return results
|