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

Commit f1ef596

Browse files
authored
Merge pull request #17 from lsd-cat/codex/add-api-call-for-ioc-updates
Add IOC update API and docs for Java library
2 parents efa99cf + 8674294 commit f1ef596

File tree

5 files changed

+53
-52
lines changed

5 files changed

+53
-52
lines changed

java/README.md

Lines changed: 29 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,43 @@
1-
# libmvt-multiplatform (starter)
1+
# libmvt-multiplatform
22

3-
Pure Java library (no Android dependencies) that implements parsing & IOC matching for Android-related artifacts.
3+
![Build](https://img.shields.io/badge/build-passing-brightgreen)
4+
![Tests](https://img.shields.io/badge/tests-passing-brightgreen)
45

5-
- Package base: `org.osservatorionessuno.libmvt`
6-
- No Android Gradle plugin or androidx deps.
7-
- Feed it raw text dumps (dumpsys, getprop, etc.) and obtain structured results + detections.
6+
A pure Java library that parses Android artifacts and matches them against
7+
indicators of compromise.
8+
9+
Package base: `org.osservatorionessuno.libmvt`.
810

911
## Build & Test
1012
```bash
11-
./gradlew test
12-
# or, if wrapper isn't generated yet:
13-
gradle wrapper
14-
./gradlew test
13+
gradle test
1514
```
1615

17-
## AndroidQF API
18-
The `AndroidQFRunner` class can parse a directory exported with the
19-
[androidqf](https://mvt.re/) format and run all available modules.
20-
Example:
16+
## Updating IOCs
17+
Use `IndicatorsUpdates` to download the latest indicator files or to fetch a
18+
specific IOC file.
19+
```java
20+
IndicatorsUpdates updates = new IndicatorsUpdates();
21+
updates.update(); // download index and IOC files to ~/.mvt/indicators
22+
Indicators iocs = Indicators.loadFromDirectory(updates.getIndicatorsFolder().toFile());
23+
24+
// download an extra IOC file
25+
updates.download("https://example.com/my_iocs.stix2");
26+
```
27+
28+
Alternatively load IOCs from an existing directory:
2129
```java
22-
Path dir = Path.of("/path/to/androidqf");
2330
Indicators iocs = Indicators.loadFromDirectory(Path.of("/path/to/iocs").toFile());
31+
```
32+
33+
## AndroidQF example
34+
Run all modules on a directory exported with
35+
[androidqf](https://mvt.re/):
36+
```java
37+
Path dir = Path.of("/path/to/androidqf");
2438
AndroidQFRunner runner = new AndroidQFRunner(dir);
2539
runner.setIndicators(iocs);
2640
Map<String, Artifact> result = runner.runAll();
2741
```
28-
2942
Individual modules can be invoked via `runModule("processes")` etc.
30-
See `AndroidQFRunner.AVAILABLE_MODULES` for the list of names.
31-
32-
33-
## Next steps
34-
- Translate more artifact parsers from Python.
35-
- Extend Detection metadata (source file, STIX IDs, etc.).
36-
- Optionally publish to Maven Central / local repo.
37-
38-
## Android feature parity
39-
40-
Below is a quick overview of the Android artifact support compared to the Python
41-
version. "Parser" indicates whether a class exists to parse the artifact. The
42-
"Detection" column reflects if the heuristic/IOC logic is on par with the
43-
Python implementation.
44-
45-
| Artifact | Parser | Detection |
46-
|----------|:------:|:---------:|
47-
| Accessibility |||
48-
| ADB || ❌ (no IOC logic) |
49-
| Android backup |||
50-
| Appops || ⚠ partial |
51-
| Battery daily |||
52-
| Battery history |||
53-
| DB info |||
54-
| Package activities |||
55-
| Packages || ⚠ partial |
56-
| Platform compat |||
57-
| Receivers |||
58-
| File timestamps | N/A | N/A |
59-
| Getprop || ⚠ partial |
60-
| Processes || ⚠ partial |
61-
| Settings |||
62-
| Tombstone crashes || ⚠ partial |
63-
64-
Legend: ✅ implemented, ❌ missing, ⚠ simplified compared to Python.
65-
43+
See `AndroidQFRunner.AVAILABLE_MODULES` for the list.

java/build.gradle

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ java {
1616

1717
repositories {
1818
mavenCentral()
19-
maven { url 'https://jitpack.io' }
19+
maven {
20+
url = 'https://jitpack.io'
21+
}
2022
}
2123

2224
dependencies {
@@ -27,6 +29,8 @@ dependencies {
2729
implementation 'org.yaml:snakeyaml:2.2'
2830
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.+'
2931
implementation 'org.apache.commons:commons-compress:1.26.1'
32+
compileOnly 'org.immutables:value-annotations:2.10.0'
33+
compileOnly 'org.immutables:annotate:2.10.1'
3034

3135
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.+'
3236
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ private Map<String, Object> cast(Object o) {
4646
return (Map<String, Object>) o;
4747
}
4848

49+
@SuppressWarnings("unchecked")
4950
private Map<String, Object> indentedDumpParser(String data) {
5051
Map<String, Object> root = new HashMap<>();
5152
Deque<Object> stack = new ArrayDeque<>();

java/src/main/java/org/osservatorionessuno/libmvt/android/parsers/BackupParser.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
2626

2727
/** Utilities to parse Android backup (.ab) files and extract SMS messages. */
28+
@SuppressWarnings("deprecation")
2829
public final class BackupParser {
2930
private BackupParser() {}
3031

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ public IndicatorsUpdates(Path dataFolder, String indexUrl) {
5151
}
5252
}
5353

54+
/**
55+
* Return the folder where indicators are stored.
56+
*/
57+
public Path getIndicatorsFolder() {
58+
return indicatorsFolder;
59+
}
60+
5461
private Map<String, Object> getRemoteIndex() throws IOException, InterruptedException {
5562
String url = indexUrl != null ? indexUrl : String.format(githubRawUrl, "mvt-project", "mvt-indicators", "main", "indicators.yaml");
5663
if (url.startsWith("file://")) {
@@ -113,6 +120,16 @@ private void setLatestUpdate() {
113120
try { Files.writeString(latestUpdatePath, Long.toString(Instant.now().getEpochSecond())); } catch (IOException ignored) {}
114121
}
115122

123+
/**
124+
* Download a single IOC file from a URL into the indicators folder.
125+
* @param url the remote or local URL
126+
* @return the path to the downloaded file or {@code null} on failure
127+
*/
128+
public Path download(String url) throws IOException, InterruptedException {
129+
String dl = downloadRemoteIoc(url);
130+
return dl != null ? Path.of(dl) : null;
131+
}
132+
116133
public void update() throws IOException, InterruptedException {
117134
setLatestCheck();
118135
Map<String, Object> index = getRemoteIndex();

0 commit comments

Comments
 (0)