spytrap_adb/
scan.rs

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        // TODO: maybe `cmd package list packages -f`
85        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                // TODO: maybe fetch apk and inspect eg. cert
100
101                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                // fetch infos about package
119                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}