|
| 1 | +package org.osservatorionessuno.libmvt.android; |
| 2 | + |
| 3 | +import org.osservatorionessuno.libmvt.android.artifacts.*; |
| 4 | +import org.osservatorionessuno.libmvt.common.Artifact; |
| 5 | +import org.osservatorionessuno.libmvt.common.Indicators; |
| 6 | + |
| 7 | +import java.io.IOException; |
| 8 | +import java.nio.file.Files; |
| 9 | +import java.nio.file.Path; |
| 10 | +import java.util.*; |
| 11 | + |
| 12 | +/** |
| 13 | + * Simple helper to run the available AndroidQF artifact parsers on a folder |
| 14 | + * containing extracted androidqf data. |
| 15 | + */ |
| 16 | +public class AndroidQFRunner { |
| 17 | + private final Path directory; |
| 18 | + private Indicators indicators; |
| 19 | + |
| 20 | + public AndroidQFRunner(Path directory) { |
| 21 | + this.directory = directory; |
| 22 | + } |
| 23 | + |
| 24 | + /** Assign indicators to use for IOC matching. */ |
| 25 | + public void setIndicators(Indicators indicators) { |
| 26 | + this.indicators = indicators; |
| 27 | + } |
| 28 | + |
| 29 | + /** Run all known modules on the provided directory. */ |
| 30 | + public Map<String, Artifact> runAll() throws Exception { |
| 31 | + Map<String, Artifact> map = new LinkedHashMap<>(); |
| 32 | + for (String name : AVAILABLE_MODULES) { |
| 33 | + Artifact art = runModule(name); |
| 34 | + if (art != null) { |
| 35 | + map.put(name, art); |
| 36 | + } |
| 37 | + } |
| 38 | + return map; |
| 39 | + } |
| 40 | + |
| 41 | + /** Run a single module by name. */ |
| 42 | + public Artifact runModule(String moduleName) throws Exception { |
| 43 | + return runModule(moduleName, this.directory); |
| 44 | + } |
| 45 | + |
| 46 | + /** Run a single module on a custom directory. */ |
| 47 | + public Artifact runModule(String moduleName, Path dir) throws Exception { |
| 48 | + return switch (moduleName) { |
| 49 | + case "dumpsys_accessibility" -> runDumpsysSection(dir, new DumpsysAccessibility(), |
| 50 | + "DUMP OF SERVICE accessibility:"); |
| 51 | + case "dumpsys_activities" -> runDumpsysSection(dir, new DumpsysPackageActivities(), |
| 52 | + "DUMP OF SERVICE package:"); |
| 53 | + case "dumpsys_receivers" -> runDumpsysSection(dir, new DumpsysReceivers(), |
| 54 | + "DUMP OF SERVICE package:"); |
| 55 | + case "dumpsys_adb" -> runDumpsysSection(dir, new DumpsysAdb(), |
| 56 | + "DUMP OF SERVICE adb:"); |
| 57 | + case "dumpsys_appops" -> runDumpsysSection(dir, new DumpsysAppops(), |
| 58 | + "DUMP OF SERVICE appops:"); |
| 59 | + case "dumpsys_battery_daily" -> runDumpsysSection(dir, new DumpsysBatteryDaily(), |
| 60 | + "DUMP OF SERVICE batterystats:"); |
| 61 | + case "dumpsys_battery_history" -> runDumpsysSection(dir, new DumpsysBatteryHistory(), |
| 62 | + "DUMP OF SERVICE batterystats:"); |
| 63 | + case "dumpsys_dbinfo" -> runDumpsysSection(dir, new DumpsysDBInfo(), |
| 64 | + "DUMP OF SERVICE dbinfo:"); |
| 65 | + case "dumpsys_packages" -> runDumpsysSection(dir, new DumpsysPackages(), |
| 66 | + "DUMP OF SERVICE package:"); |
| 67 | + case "dumpsys_platform_compat" -> runDumpsysSection(dir, new DumpsysPlatformCompat(), |
| 68 | + "DUMP OF SERVICE platform_compat:"); |
| 69 | + case "processes" -> runSimpleFile(dir, "ps.txt", new Processes()); |
| 70 | + case "getprop" -> runSimpleFile(dir, "getprop.txt", new GetProp()); |
| 71 | + case "settings" -> runSettings(dir); |
| 72 | + default -> throw new IllegalArgumentException("Unknown module: " + moduleName); |
| 73 | + }; |
| 74 | + } |
| 75 | + |
| 76 | + private Artifact finalizeArtifact(AndroidArtifact art) { |
| 77 | + if (indicators != null) { |
| 78 | + art.setIndicators(indicators); |
| 79 | + art.checkIndicators(); |
| 80 | + } |
| 81 | + return art; |
| 82 | + } |
| 83 | + |
| 84 | + private Artifact runDumpsysSection(Path dir, AndroidArtifact art, String header) throws Exception { |
| 85 | + Path file = dir.resolve("dumpsys.txt"); |
| 86 | + if (!Files.exists(file)) return null; |
| 87 | + String dumpsys = Files.readString(file); |
| 88 | + String section = extractSection(dumpsys, header); |
| 89 | + art.parse(section); |
| 90 | + return finalizeArtifact(art); |
| 91 | + } |
| 92 | + |
| 93 | + private Artifact runSimpleFile(Path dir, String name, AndroidArtifact art) throws Exception { |
| 94 | + Path file = dir.resolve(name); |
| 95 | + if (!Files.exists(file)) return null; |
| 96 | + String data = Files.readString(file); |
| 97 | + art.parse(data); |
| 98 | + return finalizeArtifact(art); |
| 99 | + } |
| 100 | + |
| 101 | + private Artifact runSettings(Path dir) throws Exception { |
| 102 | + List<Path> files; |
| 103 | + try (var stream = Files.list(dir)) { |
| 104 | + files = stream.filter(p -> p.getFileName().toString().startsWith("settings_") |
| 105 | + && p.getFileName().toString().endsWith(".txt")).toList(); |
| 106 | + } |
| 107 | + if (files.isEmpty()) return null; |
| 108 | + StringBuilder sb = new StringBuilder(); |
| 109 | + for (Path f : files) { |
| 110 | + sb.append(Files.readString(f)).append("\n"); |
| 111 | + } |
| 112 | + Settings settings = new Settings(); |
| 113 | + settings.parse(sb.toString()); |
| 114 | + return finalizeArtifact(settings); |
| 115 | + } |
| 116 | + |
| 117 | + private static String extractSection(String dumpsys, String header) { |
| 118 | + List<String> lines = new ArrayList<>(); |
| 119 | + boolean inSection = false; |
| 120 | + String delimiter = "-".repeat(78); |
| 121 | + for (String line : dumpsys.split("\n")) { |
| 122 | + if (line.trim().equals(header)) { |
| 123 | + inSection = true; |
| 124 | + continue; |
| 125 | + } |
| 126 | + if (!inSection) continue; |
| 127 | + if (line.trim().startsWith(delimiter)) break; |
| 128 | + lines.add(line); |
| 129 | + } |
| 130 | + return String.join("\n", lines); |
| 131 | + } |
| 132 | + |
| 133 | + /** List of all module names understood by the runner. */ |
| 134 | + public static final List<String> AVAILABLE_MODULES = List.of( |
| 135 | + "dumpsys_accessibility", |
| 136 | + "dumpsys_activities", |
| 137 | + "dumpsys_receivers", |
| 138 | + "dumpsys_adb", |
| 139 | + "dumpsys_appops", |
| 140 | + "dumpsys_battery_daily", |
| 141 | + "dumpsys_battery_history", |
| 142 | + "dumpsys_dbinfo", |
| 143 | + "dumpsys_packages", |
| 144 | + "dumpsys_platform_compat", |
| 145 | + "processes", |
| 146 | + "getprop", |
| 147 | + "settings" |
| 148 | + ); |
| 149 | +} |
0 commit comments