1use crate::accessibility;
2use crate::args;
3use crate::dumpsys;
4use crate::errors::*;
5use crate::ioc::{Suspicion, SuspicionLevel};
6use crate::package;
7use crate::pm;
8use crate::remote_clock;
9use crate::rules::Rules;
10use crate::settings;
11use crate::tui::Message;
12use forensic_adb::Device;
13use tokio::sync::mpsc;
14
15pub enum ScanNotifier {
16 Null,
17 Channel(mpsc::Sender<Message>),
18}
19
20impl ScanNotifier {
21 pub async fn sus(&mut self, sus: Suspicion) -> Result<()> {
22 if let ScanNotifier::Channel(tx) = self {
23 tx.send(Message::Suspicion(sus)).await?;
24 }
25 Ok(())
26 }
27
28 pub async fn app(&mut self, name: String, sus: Suspicion) -> Result<()> {
29 if let ScanNotifier::Channel(tx) = self {
30 tx.send(Message::App { name, sus }).await?;
31 }
32 Ok(())
33 }
34}
35
36pub struct Settings {
37 pub skip_apps: bool,
38}
39
40impl From<&args::Scan> for Settings {
41 fn from(args: &args::Scan) -> Settings {
42 Settings {
43 skip_apps: args.skip_apps,
44 }
45 }
46}
47
48pub async fn run(
49 device: &Device,
50 rules: &Rules,
51 scan: &Settings,
52 report: &mut ScanNotifier,
53) -> Result<()> {
54 debug!("Using device: {:?}", device);
55
56 info!("Fetching remote clock");
57 let (local_time, remote_time, drift) = remote_clock::determine(device).await?;
58 info!(
59 "Local time is {}, remote time is {}, drift={:#}",
60 local_time, remote_time, drift
61 );
62
63 info!("Enumerating android settings");
64 for (_namespace, settings) in settings::dump(device).await? {
65 for sus in settings.audit() {
66 warn!("Suspicious {:?}: {}", sus.level, sus.description);
67 report.sus(sus).await?;
68 }
69 }
70
71 info!("Enumerating service list");
72 let services = dumpsys::list_services(device).await?;
73
74 if services.contains("accessibility") {
75 info!("Reading accessibility settings");
76 let accessibility = accessibility::dump(device).await?;
77 for sus in accessibility.audit() {
78 warn!("Suspicious {:?}: {}", sus.level, sus.description);
79 report.sus(sus).await?;
80 }
81 }
82
83 if !scan.skip_apps {
84 info!("Comparing list of installed apps with known stalkerware ids");
86
87 let installed_apps = pm::list_packages(device).await?;
88 let mut progress = 0;
89 for apps in installed_apps.chunks(100) {
90 info!(
91 "Scanning installed apps ({}/{})",
92 progress,
93 installed_apps.len()
94 );
95
96 for pkg in apps {
97 progress += 1;
98
99 if let Some(name) = rules.get(&pkg.id) {
102 let alert = format!(
103 "Found known stalkerware with rule: {:?} ({:?})",
104 pkg.id, name
105 );
106 warn!("Suspicious {:?}: {}", SuspicionLevel::High, alert);
107 report
108 .app(
109 pkg.id.clone(),
110 Suspicion {
111 level: SuspicionLevel::High,
112 description: alert,
113 },
114 )
115 .await?;
116 }
117
118 let info = package::dump(device, &pkg.id).await?;
120 trace!("package infos {:?}: {:#?}", pkg.id, info);
121
122 for sus in info.audit() {
123 warn!("Suspicious {:?}: {}", sus.level, sus.description);
124 report.app(pkg.id.clone(), sus).await?;
125 }
126 }
127 }
128 }
129
130 info!("Scan finished");
131
132 Ok(())
133}