apk-v3(5)

πŸ’… apk-v3(5) β€” like, the whole entire v3 format, explained

okay bestie, buckle up, this is apk-v3 from the File Formats Manual and it's literally the blueprint for what's inside a v3 .apk file πŸŽ€


✨ NAME ✨

apk v3 β€” an overview of the apk v3 format. that's it, that's the vibe. πŸ‘‘


🌸 DESCRIPTION 🌸

A v3 .apk file contains a single package's contents, some metadata, and some signatures. no cap. πŸ’–

Inside, the .apk file holds a whole tree of objects, and that tree is represented in a custom binary format that conforms overall to a pre-defined schema. this file format is referred to inside apk(5) as "adb" β€” remember that name, you'll need it, iykyk πŸ¦‹


πŸ”₯ WIRE FORMAT πŸ”₯

so a v3 apk file is composed of sequences of serialized values, and every single one of them begins with a 32-bit little-endian word β€” that's the value's tag. 🏷️

the way it works:

  • the high 4 bits of the tag = a type code
  • the low 28 bits = an immediate value

here are the defined type codes, periodt:

  • 0x0 β€” Special (direct)
  • 0x1 β€” Int (direct)
  • 0x2 β€” Int32 (indirect)
  • 0x3 β€” Int64 (indirect)
  • 0x8 β€” Blob8 (indirect)
  • 0x9 β€” Blob16 (indirect)
  • 0xa β€” Blob32 (indirect)
  • 0xd β€” Array (indirect)
  • 0xe β€” Object (indirect)

okay so direct vs indirect, the tea β˜•:

  • a direct value is packed right into the low 28 bits of the tag word. cozy. 🫢
  • an indirect value is instead stored elsewhere in the file, and the offset of that indirect value is packed into the low 28 bits of the tag word. it's giving "address, not the thing itself" πŸ’…

Arrays and objects are represented with a sequence of numbered slots, and the value packed into their tag word is the offset at which this sequence starts. and important: the first slot is always the total number of slots, so all arrays and objects contain at least one item. no empty girlies allowed 😭

the only real difference between arrays and objects in the wire encoding is:

  • arrays are homogenous πŸ“
  • objects are heterogeneous, with a separate defined type for each slot 🌈

and the special type is used to represent three atoms:

  • 0x0 β€” NULL
  • 0x1 β€” TRUE
  • 0x2 β€” FALSE

🧚 FILE SCHEMAS 🧚

A schema is a representation of what data elements are expected in an adb file. schemas form a tree, where nodes are either:

  • scalar schemas β€” which are the leaves in the tree πŸƒ
  • array/object schemas β€” which themselves have children πŸ‘‘

for example, the schema for a package object might declare that it contains fields which themselves conform to the string array schema, or the pkginfo schema, or similar. iconic behavior honestly ✨

important: the schemas themselves are not represented in the adb file in any way. they exist in the parts of apk(1) that read and write such files. πŸ’€

a full description of all of apk's schemas would be lengthy, but as an example, here's the schema for a single file inside a package:

ADBI_FI_NAME     "name"     string
ADBI_FI_ACL      "acl"      acl
ADBI_FI_SIZE     "size"     int
ADBI_FI_MTIME    "mtime"    int
ADBI_FI_HASHES   "hash"     hexblob
ADBI_FI_TARGET   "target"   hexblob

here, all of the fields except for "acl" are scalars, and acl is itself a schema looking like:

ADBI_ACL_MODE    "mode"    oct
ADBI_ACL_USER    "user"    string
ADBI_ACL_GROUP   "group"   string

see? schemas all the way down, it's giving inception πŸŒ€


πŸŽ€ BLOCKS πŸŽ€

An actual adb file is composed of a sequence of typed blocks. a block also begins with a 32-bit little-endian tag word, which has two bits of type and 30 bits of size. πŸ”

the two type bits are:

  • 0x0 β€” ADB
  • 0x1 β€” SIG
  • 0x2 β€” DATA
  • 0x3 β€” DATAX

and the order is strict, bestie, don't mess with it:

  1. the adb file must begin with one ADB block
  2. then optionally one or more SIG blocks
  3. then one or more DATA blocks

the ADB block must begin with a magic number indicating the schema for the entire ADB block's root object. the ADB block also contains, outside the root object, some metadata describing the version of the adb format in use. πŸͺ„

πŸ’œ the SIG block πŸ’œ

The SIG block contains one or more signatures of the ADB block. signatures of the same version should be in the same SIG block. if in future a new signature version is specified, and a package should contain, for compatibility reasons, two different versions of signature during transition time, then there should be two signature blocks, one of each version. fr fr, keep them separated πŸ§β€β™€οΈπŸ§β€β™€οΈ

unlike the v2 format, the key name used for the signature is not explicitly specified. instead an intrinsic ID of the key is used for the lookup, so verifiers must find the key based in the key ID. πŸ”‘

also unlike the v2 format, the ADB block is not signed directly β€” it is hashed first by a secure digest (currently SHA512). after this, a small payload with this pre-calculated digest is signed by the given algorithm (usually the payload is then hashed again by the signing process with a secure digest based on the signature algorithm). so many layers, so much glam πŸ’…βœ¨

πŸ¦‹ the DATA blocks πŸ¦‹

The DATA blocks are used to store package file data only. all file metadata, including content hashes, is stored in the ADB block instead. πŸ“¦

so the contents of the DATA blocks are therefore protected by the hashes given in the ADB block, which is itself protected by the signature in the SIG block. a whole little trust chain, we love to see it 🌈

⚠️ heads up: it is currently illegal for a DATAX block to appear. like, do not. πŸ™ˆ

πŸ’€ NOTES πŸ’€

The v3 file format is entangled with C struct layout, since it sometimes directly writes structs into the adb section, including any compiler-added padding and such. yes, really, the padding too. we're just living like this now 😭


🌟 SEE ALSO 🌟

abuild(1), apk(8), apk-package(5), apk-v2(5) β€” go read your siblings, bestie 🫢


okay that's the whole page, you ate that πŸ’– this was apk-v3(5) from the File Formats Manual, source/version apk-v3(5), dated 2026-11-02 β€” go forth and parse some packages, queen πŸ‘‘βœ¨

πŸ“¦ where this page comes from