Pkg.CDEX — API for parsing Android CompactDex containers

Overview

The Pkg.CDEX module contains the API for parsing Android ART CompactDex (CDEX) containers and converting them back to standard DEX. CompactDex is an ART internal DEX variant introduced in Android 9 (Pie); dex2oat produces it inside VDEX containers and on-device ART consumes it natively. CDEX packs each method’s CodeItem header from 16 bytes down to 4 bytes, externalizes per-method debug_info_off values into a CompactOffsetTable, and adds a 24-byte tail to the standard DEX header. Existing DEX tools (decompilers, disassemblers, baksmali) cannot consume CompactDex directly, so this module provides a CDEXObject.ConvertToDEX() method that rebuilds a standard DEX file. When the CDEX is being scanned inside a parent VDEX, the parent’s quickening info is forwarded automatically and quickened bytecode is unquickened in place.

Parsing a CompactDex File

The following code example demonstrates how to parse a CompactDex container, inspect its header and method count, and convert it to a standard DEX:

from Pro.Core import *
from Pkg.CDEX import *

def cdexToDex(in_fname, out_fname):
    c = createContainerFromFile(in_fname)
    if c.isNull():
        return
    obj = CDEXObject()
    if not obj.Load(c) or not obj.Initialize():
        return
    print("version:", obj.GetVersion(),
          "methods:", obj.GetMethodCount(),
          "classes:", obj.GetClassDefCount())
    dex = obj.ConvertToDEX()
    if not dex.isNull() and dex.size() > 0:
        dex.save(out_fname)

Module API

Pkg.CDEX module API.

Classes:

CDEXObject()

This class represents an Android ART CompactDex (CDEX) container.

class CDEXObject

Bases: Pro.Core.CFFObject

This class represents an Android ART CompactDex (CDEX) container.

CompactDex is an ART internal DEX variant introduced in Android 9 (Pie). It packs each method’s CodeItem header from 16 bytes down to 4 bytes, externalizes per-method debug_info_off values into a CompactOffsetTable, and adds a 24-byte tail to the standard DEX header. CompactDex files are produced by dex2oat and embedded inside VDEX containers; on-device they are never extracted as standalone files. This class parses the CompactDex layout and exposes a ConvertToDEX() method that rebuilds a standard DEX file consumable by existing DEX tools.

Methods:

ConvertToDEX([quickening_info])

Converts the CompactDex into a standard DEX file consumable by existing DEX tools.

DecodeCodeItem(code_off)

Decodes a packed CompactDex CodeItem header at the given offset.

GetClassDef(idx)

Returns the parsed class definition at the given zero-based index as a dictionary with keys class_idx, access_flags, superclass_idx, interfaces_off, source_file_idx, annotations_off, class_data_off, static_values_off.

GetClassDefCount()

Returns the number of class definitions declared in the file.

GetDebugInfoOffset(method_idx)

Returns the per-method debug_info_off decoded from the CompactOffsetTable.

GetHeader()

Returns a dictionary with every parsed CompactDex header field.

GetMapItems()

Returns the parsed entries of the DEX map_list section.

GetMethodCodeOffsets()

Returns the list of methods that carry a CodeItem.

GetMethodCount()

Returns the number of method ids declared in the file.

GetVersion()

Returns the CompactDex version string.

IndexToString(idx)

Resolves a string index to its UTF-8 string value.

TypeIndexToString(idx)

Resolves a type index to its descriptor string (e.g.

ConvertToDEX(quickening_info: Optional[bytes] = None)Pro.Core.NTContainer

Converts the CompactDex into a standard DEX file consumable by existing DEX tools.

The conversion expands each cdexCode back to a regular dexCode, splices in the per-method debug_info_off from the CompactOffsetTable, transforms the header, rewrites class data code_off ulebs, updates downstream offsets and recomputes the SHA-1 signature plus Adler-32 checksum. When quickening_info is supplied (typically forwarded by a parent VDEX scan provider), quickened opcodes are unquickened in place.

Parameters

quickening_info (Optional[bytes]) – Optional VDEX quickening info blob.

Returns

Returns the rebuilt DEX as an NTContainer, or an invalid container on failure.

Return type

NTContainer

DecodeCodeItem(code_off: int)Tuple[int, int, int, int, int, int]

Decodes a packed CompactDex CodeItem header at the given offset.

The result is (registers_size, ins_size, outs_size, tries_size, insns_size, preheader_size_bytes). The preheader (when present) is read from the words preceding code_off.

Parameters

code_off (int) – The offset of the CodeItem in the file.

Returns

Returns the decoded fields.

Return type

Tuple[int, int, int, int, int, int]

GetClassDef(idx: int)Optional[Dict[str, int]]

Returns the parsed class definition at the given zero-based index as a dictionary with keys class_idx, access_flags, superclass_idx, interfaces_off, source_file_idx, annotations_off, class_data_off, static_values_off. Returns None if the index is out of range.

Parameters

idx (int) – The zero-based class definition index.

Returns

Returns the class definition fields, or None.

Return type

Optional[Dict[str, int]]

GetClassDefCount()int

Returns the number of class definitions declared in the file.

Returns

Returns the class def count.

Return type

int

GetDebugInfoOffset(method_idx: int)int

Returns the per-method debug_info_off decoded from the CompactOffsetTable.

Returns 0 if the method has no debug info, or if the index is out of range.

Parameters

method_idx (int) – The zero-based method index.

Returns

Returns the debug info offset.

Return type

int

GetHeader()Dict[str, Any]

Returns a dictionary with every parsed CompactDex header field.

Standard DEX header fields are present (magic, checksum, signature, file_size, header_size, endian_tag, link_size, link_off, map_off, string_ids_size/off, type_ids_size/off, proto_ids_size/off, field_ids_size/off, method_ids_size/off, class_defs_size/off, data_size, data_off) along with CompactDex-specific fields (feature_flags, debug_info_offsets_pos, debug_info_offsets_table_offset, debug_info_base, owned_data_begin, owned_data_end).

Returns

Returns the header dictionary.

Return type

Dict[str, Any]

GetMapItems()List[Dict[str, int]]

Returns the parsed entries of the DEX map_list section.

Each entry is a dictionary with type, size and offset keys.

Returns

Returns the list of map items.

Return type

List[Dict[str, int]]

GetMethodCodeOffsets()List[Tuple[int, int]]

Returns the list of methods that carry a CodeItem.

Each tuple is (method_idx, code_off) where code_off points at a packed cdexCode structure inside the data section.

Returns

Returns the list of (method index, code offset) pairs.

Return type

List[Tuple[int, int]]

GetMethodCount()int

Returns the number of method ids declared in the file.

Returns

Returns the method count.

Return type

int

GetVersion()str

Returns the CompactDex version string. Currently always "001".

Returns

Returns the version string.

Return type

str

IndexToString(idx: int)str

Resolves a string index to its UTF-8 string value. Returns an empty string when the index is out of range or the string cannot be decoded.

Parameters

idx (int) – The zero-based string index.

Returns

Returns the decoded string.

Return type

str

TypeIndexToString(idx: int)str

Resolves a type index to its descriptor string (e.g. "Lcom/foo/Bar;"). Returns an empty string when the index is out of range.

Parameters

idx (int) – The zero-based type index.

Returns

Returns the type descriptor.

Return type

str