Skip to content
This repository was archived by the owner on Aug 28, 2025. It is now read-only.

Commit 5c36fb2

Browse files
authored
Merge pull request #14 from lsd-cat/xcyiwx-codex/align-java-detection-logic-with-python
Align Android Java indicators with Python
2 parents c41fe97 + c08c332 commit 5c36fb2

File tree

6 files changed

+119
-32
lines changed

6 files changed

+119
-32
lines changed

java/src/main/java/org/osservatorionessuno/libmvt/android/artifacts/DumpsysAccessibility.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ public void checkIndicators() {
6161
for (Object obj : results) {
6262
@SuppressWarnings("unchecked")
6363
Map<String, String> record = (Map<String, String>) obj;
64-
String context = record.get("service");
65-
detected.addAll(indicators.matchString(context, IndicatorType.PROCESS));
64+
String pkg = record.get("package_name");
65+
detected.addAll(indicators.matchString(pkg, IndicatorType.APP_ID));
6666
}
6767
}
6868
}

java/src/main/java/org/osservatorionessuno/libmvt/android/artifacts/DumpsysPackages.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,26 @@ public class DumpsysPackages extends AndroidArtifact {
1717
"eu.chainfire.supersu",
1818
"com.koushikdutta.superuser",
1919
"com.thirdparty.superuser",
20-
"com.yellowes.su"
20+
"com.yellowes.su",
21+
"com.koushikdutta.rommanager",
22+
"com.koushikdutta.rommanager.license",
23+
"com.dimonvideo.luckypatcher",
24+
"com.chelpus.lackypatch",
25+
"com.ramdroid.appquarantine",
26+
"com.ramdroid.appquarantinepro",
27+
"com.devadvance.rootcloak",
28+
"com.devadvance.rootcloakplus",
29+
"de.robv.android.xposed.installer",
30+
"com.saurik.substrate",
31+
"com.zachspong.temprootremovejb",
32+
"com.amphoras.hidemyroot",
33+
"com.amphoras.hidemyrootadfree",
34+
"com.formyhm.hiderootPremium",
35+
"com.formyhm.hideroot",
36+
"me.phh.superuser",
37+
"eu.chainfire.supersu.pro",
38+
"com.kingouser.com",
39+
"com.topjohnwu.magisk"
2140
);
2241

2342
private static class PackageDetails {
@@ -174,15 +193,16 @@ public void parse(String content) {
174193

175194
@Override
176195
public void checkIndicators() {
177-
if (indicators == null) return;
178196
for (Object obj : results) {
179197
@SuppressWarnings("unchecked")
180198
Map<String, Object> record = (Map<String, Object>) obj;
181199
String pkg = (String) record.get("package_name");
182200
if (ROOT_PACKAGES.contains(pkg)) {
183201
detected.add(new Detection(IndicatorType.PROCESS, pkg, "root_package"));
184202
}
185-
detected.addAll(indicators.matchString(pkg, IndicatorType.APP_ID));
203+
if (indicators != null) {
204+
detected.addAll(indicators.matchString(pkg, IndicatorType.APP_ID));
205+
}
186206
}
187207
}
188208
}

java/src/main/java/org/osservatorionessuno/libmvt/common/Indicators.java

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,20 @@ public class Indicators {
2121
private final Trie processTrie;
2222
private final Trie appIdTrie;
2323
private final Trie propertyTrie;
24+
private final List<String> processList;
25+
private final List<String> appIdList;
26+
private final List<String> propertyList;
2427

25-
private Indicators(Trie domainTrie, Trie urlTrie, Trie processTrie, Trie appIdTrie, Trie propertyTrie) {
28+
private Indicators(Trie domainTrie, Trie urlTrie, Trie processTrie, Trie appIdTrie, Trie propertyTrie,
29+
List<String> processList, List<String> appIdList, List<String> propertyList) {
2630
this.domainTrie = domainTrie;
2731
this.urlTrie = urlTrie;
2832
this.processTrie = processTrie;
2933
this.appIdTrie = appIdTrie;
3034
this.propertyTrie = propertyTrie;
35+
this.processList = processList;
36+
this.appIdList = appIdList;
37+
this.propertyList = propertyList;
3138
}
3239

3340
public static Indicators loadFromDirectory(File dir) throws IOException {
@@ -37,9 +44,12 @@ public static Indicators loadFromDirectory(File dir) throws IOException {
3744
Trie.TrieBuilder processes = Trie.builder().ignoreCase();
3845
Trie.TrieBuilder appIds = Trie.builder().ignoreCase();
3946
Trie.TrieBuilder properties = Trie.builder().ignoreCase();
47+
List<String> procList = new ArrayList<>();
48+
List<String> appIdList = new ArrayList<>();
49+
List<String> propList = new ArrayList<>();
4050

4151
File[] files = dir.listFiles((d, name) -> name.endsWith(".json") || name.endsWith(".stix2"));
42-
if (files == null) return new Indicators(domains.build(), urls.build(), processes.build(), appIds.build(), properties.build());
52+
if (files == null) return new Indicators(domains.build(), urls.build(), processes.build(), appIds.build(), properties.build(), procList, appIdList, propList);
4353

4454
for (File f : files) {
4555
if (f.getName().endsWith(".stix2")) {
@@ -48,7 +58,8 @@ public static Indicators loadFromDirectory(File dir) throws IOException {
4858
BundleObject bundle = StixParsers.parseBundle(json);
4959
for (BundleableObject obj : bundle.getObjects()) {
5060
if (obj instanceof IndicatorSdo ind) {
51-
addPattern(domains, urls, processes, appIds, properties, ind.getPattern());
61+
addPattern(domains, urls, processes, appIds, properties,
62+
procList, appIdList, propList, ind.getPattern());
5263
}
5364
}
5465
} catch (Exception ex) {
@@ -58,7 +69,9 @@ public static Indicators loadFromDirectory(File dir) throws IOException {
5869
if (objects != null && objects.isArray()) {
5970
for (JsonNode node : objects) {
6071
if ("indicator".equals(node.path("type").asText())) {
61-
addPattern(domains, urls, processes, appIds, properties, node.path("pattern").asText());
72+
addPattern(domains, urls, processes, appIds, properties,
73+
procList, appIdList, propList,
74+
node.path("pattern").asText());
6275
}
6376
}
6477
}
@@ -68,21 +81,24 @@ public static Indicators loadFromDirectory(File dir) throws IOException {
6881
JsonNode arr = root.get("indicators");
6982
if (arr == null) continue;
7083
for (JsonNode coll : arr) {
71-
addField(domains, coll, "domain-name:value");
72-
addField(domains, coll, "ipv4-addr:value");
73-
addField(urls, coll, "url:value");
74-
addField(processes, coll, "process:name");
75-
addField(appIds, coll, "app:id");
76-
addField(properties, coll, "android-property:name");
84+
addField(domains, coll, "domain-name:value", null);
85+
addField(domains, coll, "ipv4-addr:value", null);
86+
addField(urls, coll, "url:value", null);
87+
addField(processes, coll, "process:name", procList);
88+
addField(appIds, coll, "app:id", appIdList);
89+
addField(properties, coll, "android-property:name", propList);
7790
}
7891
}
7992
}
80-
return new Indicators(domains.build(), urls.build(), processes.build(), appIds.build(), properties.build());
93+
return new Indicators(domains.build(), urls.build(), processes.build(), appIds.build(), properties.build(),
94+
procList, appIdList, propList);
8195
}
8296

8397
private static void addPattern(Trie.TrieBuilder domains, Trie.TrieBuilder urls,
8498
Trie.TrieBuilder processes, Trie.TrieBuilder appIds,
85-
Trie.TrieBuilder properties, String pattern) {
99+
Trie.TrieBuilder properties,
100+
List<String> procList, List<String> appIdList, List<String> propList,
101+
String pattern) {
86102
if (pattern == null) return;
87103
String p = pattern.trim();
88104
if (p.startsWith("[") && p.endsWith("]")) {
@@ -98,22 +114,23 @@ private static void addPattern(Trie.TrieBuilder domains, Trie.TrieBuilder urls,
98114
switch (key) {
99115
case "domain-name:value", "ipv4-addr:value" -> domains.addKeyword(value.toLowerCase());
100116
case "url:value" -> urls.addKeyword(value.toLowerCase());
101-
case "process:name" -> processes.addKeyword(value.toLowerCase());
102-
case "app:id" -> appIds.addKeyword(value.toLowerCase());
103-
case "android-property:name" -> properties.addKeyword(value.toLowerCase());
117+
case "process:name" -> { processes.addKeyword(value.toLowerCase()); procList.add(value.toLowerCase()); }
118+
case "app:id" -> { appIds.addKeyword(value.toLowerCase()); appIdList.add(value.toLowerCase()); }
119+
case "android-property:name" -> { properties.addKeyword(value.toLowerCase()); propList.add(value.toLowerCase()); }
104120
default -> {
105121
}
106122
}
107123
}
108124

109-
private static void addField(Trie.TrieBuilder builder, JsonNode coll, String key) {
125+
private static void addField(Trie.TrieBuilder builder, JsonNode coll, String key, List<String> store) {
110126
if (coll == null) return;
111127
JsonNode node = coll.get(key);
112128
if (node == null || node.isNull()) return;
113129
for (JsonNode value : iterable(node)) {
114130
String s = value.asText();
115131
if (s != null && !s.isBlank()) {
116132
builder.addKeyword(s.toLowerCase());
133+
if (store != null) store.add(s.toLowerCase());
117134
}
118135
}
119136
}
@@ -125,16 +142,40 @@ private static Iterable<JsonNode> iterable(JsonNode node) {
125142

126143
public List<Detection> matchString(String s, IndicatorType type) {
127144
if (s == null) return List.of();
128-
Trie trie = switch (type) {
129-
case DOMAIN -> domainTrie;
130-
case URL -> urlTrie;
131-
case PROCESS -> processTrie;
132-
case APP_ID -> appIdTrie;
133-
case PROPERTY -> propertyTrie;
134-
};
145+
String lower = s.toLowerCase();
135146
List<Detection> detections = new ArrayList<>();
136-
for (Emit e : trie.parseText(s.toLowerCase())) {
137-
detections.add(new Detection(type, e.getKeyword(), s));
147+
switch (type) {
148+
case DOMAIN -> {
149+
for (Emit e : domainTrie.parseText(lower)) {
150+
detections.add(new Detection(type, e.getKeyword(), s));
151+
}
152+
}
153+
case URL -> {
154+
for (Emit e : urlTrie.parseText(lower)) {
155+
detections.add(new Detection(type, e.getKeyword(), s));
156+
}
157+
}
158+
case PROCESS -> {
159+
for (String kw : processList) {
160+
if (kw.equals(lower) || (lower.length() == 16 && kw.startsWith(lower))) {
161+
detections.add(new Detection(type, kw, s));
162+
}
163+
}
164+
}
165+
case APP_ID -> {
166+
for (String kw : appIdList) {
167+
if (kw.equals(lower)) {
168+
detections.add(new Detection(type, kw, s));
169+
}
170+
}
171+
}
172+
case PROPERTY -> {
173+
for (String kw : propertyList) {
174+
if (kw.equals(lower)) {
175+
detections.add(new Detection(type, kw, s));
176+
}
177+
}
178+
}
138179
}
139180
return detections;
140181
}

java/src/test/java/org/osservatorionessuno/libmvt/android/artifacts/DumpsysAccessibilityTest.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,15 @@ public void testIocCheck() throws Exception {
4646
DumpsysAccessibility da = new DumpsysAccessibility();
4747
String data = readResource("android_data/dumpsys_accessibility.txt");
4848
da.parse(data);
49-
Indicators indicators = Indicators.loadFromDirectory(Path.of("src", "test", "resources", "iocs").toFile());
49+
50+
Path temp = Files.createTempDirectory("iocs");
51+
Files.list(Path.of("src","test","resources","iocs"))
52+
.forEach(p -> { try { Files.copy(p, temp.resolve(p.getFileName())); } catch (Exception ignored) {} });
53+
Files.writeString(temp.resolve("extra.json"), "{\"indicators\":[{\"app:id\":[\"com.sec.android.app.camera\"]}]}" );
54+
Indicators indicators = Indicators.loadFromDirectory(temp.toFile());
5055
da.setIndicators(indicators);
5156
da.checkIndicators();
5257
assertEquals(1, da.getDetected().size());
53-
assertEquals(IndicatorType.PROCESS, da.getDetected().get(0).type());
58+
assertEquals(IndicatorType.APP_ID, da.getDetected().get(0).type());
5459
}
5560
}

java/src/test/java/org/osservatorionessuno/libmvt/android/artifacts/DumpsysPackagesTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,13 @@ public void testIocCheck() throws Exception {
3838
dpa.checkIndicators();
3939
assertTrue(dpa.getDetected().size() > 0);
4040
}
41+
42+
@Test
43+
public void testRootPackageDetection() {
44+
DumpsysPackages dpa = new DumpsysPackages();
45+
String sample = "Packages:\n Package [com.topjohnwu.magisk] (test)\n userId=0\n";
46+
dpa.parse(sample);
47+
dpa.checkIndicators();
48+
assertFalse(dpa.getDetected().isEmpty());
49+
}
4150
}

java/src/test/java/org/osservatorionessuno/libmvt/android/artifacts/ProcessesTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,16 @@ public void testIocCheck() throws Exception {
3737
p.checkIndicators();
3838
assertTrue(p.getDetected().size() > 0);
3939
}
40+
41+
@Test
42+
public void testTruncatedProcessMatch() throws Exception {
43+
Processes p = new Processes();
44+
String data = "USER PID PPID VSZ RSS WCHAN ADDR S NAME\n" +
45+
"root 50 2 0 0 0 0 S com.bad.actor.ma\n";
46+
p.parse(data);
47+
Indicators indicators = Indicators.loadFromDirectory(Path.of("src", "test", "resources", "iocs").toFile());
48+
p.setIndicators(indicators);
49+
p.checkIndicators();
50+
assertFalse(p.getDetected().isEmpty());
51+
}
4052
}

0 commit comments

Comments
 (0)