Comprehensive Python toolkit for Android application reverse engineering and security analysis.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Binary Android XML (AXML) and Android Resource (ARSC) file processing for accessing application resources, layouts, configuration data, and string resources. Android applications store XML files in a binary format for efficiency.
Binary XML parser for converting Android's binary XML format back to standard XML.
class AXMLPrinter:
def __init__(self, raw_buff: bytes):
"""
Initialize AXML parser.
Parameters:
- raw_buff: Raw AXML file bytes
"""
def get_xml(self, pretty: bool = True) -> bytes:
"""
Convert AXML to XML format.
Parameters:
- pretty: Format XML with indentation
Returns:
XML content as bytes
"""
def is_valid(self) -> bool:
"""Return True if AXML structure is valid."""
def get_xml_obj(self):
"""Return parsed XML as ElementTree object."""
def get_buff(self) -> bytes:
"""Return original AXML buffer."""Low-level AXML parsing functionality for detailed binary XML analysis.
class AXMLParser:
def __init__(self, raw_buff: bytes):
"""
Initialize low-level AXML parser.
Parameters:
- raw_buff: Raw AXML file bytes
"""
def next(self) -> int:
"""
Advance to next XML event.
Returns:
Event type constant (START_DOCUMENT, START_TAG, etc.)
"""
def get_name(self) -> str:
"""Return name of current XML element."""
def get_text(self) -> str:
"""Return text content of current element."""
def get_attribute_count(self) -> int:
"""Return number of attributes for current element."""
def get_attribute_name(self, i: int) -> str:
"""
Get attribute name by index.
Parameters:
- i: Attribute index
Returns:
Attribute name string
"""
def get_attribute_value(self, i: int) -> str:
"""
Get attribute value by index.
Parameters:
- i: Attribute index
Returns:
Attribute value string
"""
def get_attribute_namespace(self, i: int) -> str:
"""Get attribute namespace by index."""
def get_attribute_resource_id(self, i: int) -> int:
"""Get attribute resource ID by index."""Access to AXML string pools and string resolution.
class StringBlock:
def __init__(self, buff: bytes, header: object):
"""
Initialize string block parser.
Parameters:
- buff: String block bytes
- header: AXML header information
"""
def get_string(self, idx: int) -> str:
"""
Get string by index from string pool.
Parameters:
- idx: String index
Returns:
String value or None if invalid
"""
def get_strings_size(self) -> int:
"""Return total number of strings in pool."""
def show(self) -> None:
"""Display string block information."""Android Resource (ARSC) file parser for accessing application resources and configurations.
class ARSCParser:
def __init__(self, raw_buff: bytes):
"""
Initialize ARSC parser.
Parameters:
- raw_buff: Raw ARSC file bytes
"""
def get_packages_names(self) -> list[str]:
"""Return list of all package names in resources."""
def get_string_resources(self, package_name: str, locale: str = '\x00\x00') -> dict[str, str]:
"""
Get string resources for package and locale.
Parameters:
- package_name: Target package name
- locale: Locale code (default is default locale)
Returns:
Dictionary mapping resource names to string values
"""
def get_id(self, package_name: str, ttype: int, name: str) -> int:
"""
Get resource ID for named resource.
Parameters:
- package_name: Package name
- ttype: Resource type ID
- name: Resource name
Returns:
Resource ID or None if not found
"""
def get_public_resources(self, package_name: str, ttype: int = None) -> dict:
"""
Get public resources for package.
Parameters:
- package_name: Package name
- ttype: Optional resource type filter
Returns:
Dictionary of public resource mappings
"""
def get_type_configs(self, package_name: str, type_name: str) -> list:
"""
Get configurations for resource type.
Parameters:
- package_name: Package name
- type_name: Resource type name
Returns:
List of configuration objects
"""Resolve resource references and values with configuration support.
class ResourceResolver:
def __init__(self, android_resources: ARSCParser, config: object = None):
"""
Initialize resource resolver.
Parameters:
- android_resources: ARSCParser instance
- config: Configuration for resolution
"""
def resolve(self, res_id: int) -> tuple:
"""
Resolve resource ID to value and configuration.
Parameters:
- res_id: Resource identifier
Returns:
Tuple of (resource_value, configuration)
"""
def get_string(self, res_id: int) -> str:
"""
Resolve resource ID to string value.
Parameters:
- res_id: String resource ID
Returns:
String value or None if not found
"""
def get_resolved_res_configs(self, res_id: int, config: object = None) -> list:
"""Get all resolved configurations for resource."""Low-level access to resource table structure and metadata.
class ARSCResTablePackage:
def get_name(self) -> str:
"""Return package name."""
def get_id(self) -> int:
"""Return package ID."""
def get_type_strings(self) -> StringBlock:
"""Return type names string block."""
def get_key_strings(self) -> StringBlock:
"""Return key names string block."""
class ARSCResType:
def get_package_name(self) -> str:
"""Return containing package name."""
def get_type(self) -> str:
"""Return resource type name."""
def get_config(self) -> object:
"""Return configuration object."""
def get_entries(self) -> dict:
"""Return dictionary of entry ID to entry object mappings."""
class ARSCResTableEntry:
def get_index(self) -> int:
"""Return entry index."""
def get_key(self) -> str:
"""Return resource key name."""
def get_value(self):
"""Return resource value."""
def is_public(self) -> bool:
"""Return True if resource is public."""
def is_complex(self) -> bool:
"""Return True if entry is complex type."""Resource configuration handling for different device configurations.
class ARSCResTableConfig:
def get_language(self) -> str:
"""Return language code."""
def get_country(self) -> str:
"""Return country code."""
def get_density(self) -> int:
"""Return screen density."""
def get_orientation(self) -> int:
"""Return screen orientation."""
def get_screen_size(self) -> int:
"""Return screen size category."""
def get_keyboard(self) -> int:
"""Return keyboard type."""
def get_qualifier(self) -> str:
"""Return full configuration qualifier string."""
def match(self, config) -> bool:
"""Check if this config matches another config."""from androguard.core.axml import AXMLPrinter
# Read AXML file
with open("AndroidManifest.xml", "rb") as f:
axml_data = f.read()
# Parse AXML
axml = AXMLPrinter(axml_data)
if axml.is_valid():
# Convert to readable XML
xml_content = axml.get_xml(pretty=True)
print(xml_content.decode('utf-8'))
# Save as regular XML file
with open("AndroidManifest_readable.xml", "wb") as f:
f.write(xml_content)
else:
print("Invalid AXML file")from androguard.core.axml import ARSCParser
# Load resources.arsc
with open("resources.arsc", "rb") as f:
arsc_data = f.read()
arsc = ARSCParser(arsc_data)
# Get all packages
packages = arsc.get_packages_names()
print(f"Packages: {packages}")
for package in packages:
print(f"\nPackage: {package}")
# Get string resources
strings = arsc.get_string_resources(package)
print(f"String resources: {len(strings)}")
for name, value in list(strings.items())[:10]: # Show first 10
print(f" {name}: {value}")
# Get public resources
public_resources = arsc.get_public_resources(package)
print(f"Public resources: {len(public_resources)}")# Get resources for different configurations
package_name = "com.example.app"
# Get all type configurations
string_configs = arsc.get_type_configs(package_name, "string")
print(f"String configurations: {len(string_configs)}")
for config in string_configs:
print(f"Configuration: {config.get_qualifier()}")
print(f" Language: {config.get_language()}")
print(f" Country: {config.get_country()}")
print(f" Density: {config.get_density()}")
# Get strings for specific locale
french_strings = arsc.get_string_resources(package_name, "fr")
if french_strings:
print("French strings:")
for name, value in list(french_strings.items())[:5]:
print(f" {name}: {value}")from androguard.core.axml import ARSCParser
# Create resolver
resolver = arsc.ResourceResolver(arsc)
# Resolve specific resource IDs
resource_ids = [0x7f040001, 0x7f050002, 0x7f060003]
for res_id in resource_ids:
try:
value, config = resolver.resolve(res_id)
print(f"Resource 0x{res_id:08x}:")
print(f" Value: {value}")
print(f" Config: {config.get_qualifier() if config else 'default'}")
except Exception as e:
print(f"Failed to resolve 0x{res_id:08x}: {e}")
# Resolve string resources
app_name_id = arsc.get_id(package_name, 0x03, "app_name") # 0x03 = string type
if app_name_id:
app_name = resolver.get_string(app_name_id)
print(f"App name: {app_name}")from androguard.core.axml import AXMLParser
# Low-level AXML parsing
parser = AXMLParser(axml_data)
# Parse events manually
while True:
event = parser.next()
if event == axml.START_DOCUMENT:
print("Document started")
elif event == axml.START_TAG:
name = parser.get_name()
print(f"Start tag: {name}")
# Process attributes
attr_count = parser.get_attribute_count()
for i in range(attr_count):
attr_name = parser.get_attribute_name(i)
attr_value = parser.get_attribute_value(i)
attr_ns = parser.get_attribute_namespace(i)
if attr_ns:
print(f" {attr_ns}:{attr_name}={attr_value}")
else:
print(f" {attr_name}={attr_value}")
elif event == axml.END_TAG:
name = parser.get_name()
print(f"End tag: {name}")
elif event == axml.TEXT:
text = parser.get_text()
if text.strip():
print(f"Text: {text}")
elif event == axml.END_DOCUMENT:
print("Document ended")
break# Analyze different resource types
for package in packages:
print(f"\nAnalyzing package: {package}")
# Get all resource types
public_resources = arsc.get_public_resources(package)
# Group by type
by_type = {}
for res_id, (res_type, res_name) in public_resources.items():
if res_type not in by_type:
by_type[res_type] = []
by_type[res_type].append((res_id, res_name))
# Display statistics
for res_type, resources in by_type.items():
print(f" {res_type}: {len(resources)} resources")
# Show a few examples
for res_id, res_name in resources[:3]:
print(f" 0x{res_id:08x}: {res_name}")def format_value(value_type: int, value_data: int, string_block: StringBlock = None) -> str:
"""
Format resource value based on its type.
Parameters:
- value_type: Resource value type constant
- value_data: Raw value data
- string_block: String block for string resolution
Returns:
Formatted value string
"""
def complexToFloat(xcomplex: int) -> float:
"""
Convert complex unit value to float.
Parameters:
- xcomplex: Complex unit value
Returns:
Float representation
"""
def get_arsc_info(arsc_file: str) -> dict:
"""
Get formatted ARSC file information.
Parameters:
- arsc_file: Path to ARSC file
Returns:
Dictionary with ARSC analysis results
"""Install with Tessl CLI
npx tessl i tessl/pypi-androguard