Skip to content

Files and resources

Reading entries

namelist() lists the entries from the zip central directory, and read() returns an entry's decompressed bytes together with how it was stored.

# names come from the central directory, in archive order
names = apk.namelist()
print(f"{len(names)} entries")

data, compression = apk.read("AndroidManifest.xml")
print(f"{len(data)} bytes, {compression}")  # e.g. "deflated"

The compression hint is "stored", "deflated", "stored_tampered" or "deflated_tampered".

println!("{} entries", apk.namelist().count());

let (data, compression) = apk.read("AndroidManifest.xml")?;
println!("{} bytes, {compression:?}", data.len());

The compression is a FileCompressionType: Stored, Deflated, StoredTampered or DeflatedTampered.

apk-info extract -l app.apk

For XAPK, APKM and APKS files, namelist() and read() work on the outer archive, so you can read manifest.json or an individual split. Pass base=True (Python) or use get_base_zip() (Rust) to read the app's own files. See XAPK, APKM and APKS.

A _tampered value means the entry's local header disagrees with the central directory, a sign of BadPack. apk-info still returns the correct data.

DEX files and native libraries

for name in apk.namelist():
    if name.startswith("classes") and name.endswith(".dex"):
        data, _ = apk.read(name)
        (out / name).write_bytes(data)
        print(f"saved {name}: {len(data)} bytes")
for name in apk.namelist() {
    if name.starts_with("classes") && name.ends_with(".dex") {
        let (data, _) = apk.read(name)?;
        std::fs::write(Path::new(&out).join(name), &data)?;
    }
}
apk-info extract app.apk -f 'classes\d*\.dex'
apk-info extract app.apk -f 'lib/.*\.so'

is_multidex() tells you whether there is more than one classes*.dex, and get_native_libraries() returns the libraries declared with <uses-native-library> in the manifest (not the .so files under lib/).

The app icon

get_application_icon() resolves android:icon to a path inside the archive:

icon = apk.get_application_icon()  # e.g. "res/mipmap-xxxhdpi-v4/ic_launcher.png"
if icon:
    data, _ = apk.read(icon)
    (out / Path(icon).name).write_bytes(data)
    # an .xml icon is an adaptive icon: binary XML that points to other drawables
    print(f"saved {icon}")

On Android 8+ many apps use an adaptive icon: an .xml file in binary XML format that references a foreground and a background drawable. Use apk-info extract -r to get it as readable XML.

Decoding resources

apk-info extract -r decodes resources the way apktool and jadx do:

apk-info extract apkstest.apk -r -o out/
out/apkstest.apk.unp/
├── AndroidManifest.xml          # readable XML
├── META-INF/...
├── lib/x86_64/libstub.so
└── res/
    ├── drawable-xxhdpi/icon.png
    ├── values/strings.xml       # decoded from resources.arsc
    ├── values-ru/strings.xml
    └── xml/
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="app_name">APKS Test</string>
</resources>
  • apk-info expands resources.arsc into res/values*/ (strings, plurals, arrays, colors, styles, …) and writes file resources to their canonical res/<type>[-<config>]/ folders.
  • It writes only the resources registered in the resource table. Release builds shorten file names (res/7j.png), and some apps add decoy files under res/; the decoded tree uses the real names and skips the decoys.
  • For XAPK, APKM and APKS files, it merges the resources of the base APK and every config split into one res/ tree, so you get all languages and screen densities in one place.
  • It copies DEX files, native libraries and assets as they are.

Locale folders use the legacy form (values-es-r419), like jadx; apktool writes values-b+es+419. Android accepts both.