Quickstart¶
This page does the same two things with each interface: print basic information about an APK, then list its signing certificates.
The examples use apkstest.apk, the small test app from the repository, but any .apk, .xapk, .apkm or .apks file works. To follow along:
The CLI on a malware sample, a ToxicPanda banker:
$ apk-info show --signatures --anomalies malware.apk
malware.apk
Package com.fmvmxi.qvhmylhz
Label Xvide
Version 1.1 (1)
SDK min 26 · target 33
Main activity -
Signatures
v2, v3 CN=Unknown,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=Unknown
Serial 14cbf84a
Valid 2018-03-17 → 2045-08-02
Algorithm sha256WithRSAEncryption
SHA-256 f67430285c020c72fbeaa81870b42c79bcc159b08be2ddd83789d0eae68215fd
SHA-1 520a87add5f61b426b7458ef05655267853c008d
MD5 0856d66bfce9027a146f9ba03fe1e9b9
Tampering YES · 2 tampered
✗ encryption_flag 3 AndroidManifest.xml, resources.arsc, … 1 more
✗ string_pool_garbage_chunk 2 AndroidManifest.xml, resources.arsc
Basic information¶
$ apk-info show apkstest.apk
apkstest.apk
Package io.github.delvinru.apkstest
Label APKS Test
Version 1.0.7 (7)
SDK min 21 · target 35
Main activity io.github.delvinru.apkstest/.MainActivity
ABIs x86_64
show accepts several files and directories at once and walks directories recursively.
from apk_info import APK
apk = APK(path)
print(f"Package: {apk.get_package_name()}")
print(f"Version: {apk.get_version_name()}")
print(f"Min SDK: {apk.get_min_sdk_version()}")
print(f"Target SDK: {apk.get_target_sdk_version()}")
print(f"Label: {apk.get_application_label()}")
print(f"Main: {apk.get_main_activity()}")
Getters return None when the manifest doesn't set the value.
let apk = Apk::new(&path)?;
println!(
"Package: {}",
apk.get_package_name().unwrap_or_default()
);
println!(
"Version: {}",
apk.get_version_name().unwrap_or_default()
);
println!(
"Min SDK: {}",
apk.get_min_sdk_version().unwrap_or_default()
);
println!("Target SDK: {}", apk.get_target_sdk_version());
println!(
"Label: {}",
apk.get_application_label().unwrap_or_default()
);
println!(
"Main: {}",
apk.get_main_activity().unwrap_or_default()
);
Getters return Option, so a missing manifest attribute is None rather than an error.
The Python and Rust examples print:
Package: io.github.delvinru.apkstest
Version: 1.0.7
Min SDK: 21
Target SDK: 35
Label: APKS Test
Main: .MainActivity
The CLI prints the main activity as a package/activity component name, ready for adb shell am start -n. The library returns the activity name as written in the manifest, which may be relative (.MainActivity).
Signatures¶
$ apk-info show --signatures apkstest.apk
...
APK Signature block:
Type: v1
Serial Number: 1bdde6531ed48e6b
Subject: CN=apk-info apks test
Issuer: CN=apk-info apks test
Valid from: 2026-10-31T18:17:58Z
Valid until: 2126-10-07T18:17:58Z
Signature type: sha384WithRSAEncryption
MD5 fingerprint: 19c9134e6e59d32b357a88a77fca527d
SHA1 fingerprint: 5a1a310e23b7a1e074be7772595c6c19d22f8b60
SHA256 fingerprint: 71c1bcf09db2254f8eadf5f68637f84f5c34acc04fe9c672393c518af68bc6f2
Type: v2
...
from apk_info import Signature
for signature in apk.get_signatures():
match signature:
case Signature.V1() | Signature.V2() | Signature.V3() | Signature.V31() | Signature.V32():
for cert in signature.certificates:
print(f"{type(signature).__name__}: {cert.subject} ({cert.sha256_fingerprint})")
case _:
print(signature)
Each scheme has its own class (Signature.V1, Signature.V2, …), so you can match on it.
for signature in apk.get_signatures()? {
match &signature {
Signature::V1(certs)
| Signature::V2(certs)
| Signature::V3(certs)
| Signature::V31(certs)
| Signature::V32(certs) => {
for cert in certs {
println!(
"{}: {} ({})",
signature.name(),
cert.subject,
cert.sha256_fingerprint
);
}
}
other => println!("{}", other.name()),
}
}
Signature::name() gives a short scheme name (v1, v3.1, Stamp Block v2, …).
Build tools sign most APKs with several schemes and one key, so the same certificate shows up once per scheme.
For split containers, get_signatures() returns the base APK's signatures, and get_container_signatures() returns the signature of the outer archive, if it has one.
JSON output¶
For scripts, show --json prints one JSON object per APK (JSONL):
$ apk-info show --json apkstest.apk | jq '{package_name, version_name, min_sdk_version}'
{
"package_name": "io.github.delvinru.apkstest",
"version_name": "1.0.7",
"min_sdk_version": "21"
}
Run the examples¶
Both examples live in the repository, and CI runs them on every change:
Next steps¶
- CLI reference: extraction, manifest decoding, repacking BadPack samples.
- Python API: permissions, components, resources and more.