Introduction: The Challenge of Resource Reconstruction
In the realm of Android application reverse engineering, understanding an application’s resources is paramount. Android packages (APKs) bundle compiled resources into a file named resources.arsc, which acts as a lookup table mapping resource IDs to their corresponding values, such as strings, layouts, drawables, and more. When reverse engineering an APK, especially one that has been obfuscated or stripped of certain metadata, the explicit mapping of resource IDs to human-readable names can be lost. This typically happens when the public.xml file, which defines public resource IDs, is omitted or pruned during the build process, often for size optimization or to hinder reverse engineering efforts. Without this crucial mapping, tools like Apktool might struggle to decompile resources accurately, leaving placeholders like <id type="id" name="0x7f030005" /> instead of meaningful names like <id type="id" name="activity_main" />. This article details an expert-level approach to automate the reconstruction of Android resources using Python scripts, leveraging the Android Asset Packaging Tool (AAPT2) to generate a functional public.xml for better decompilation.
Understanding Android Resources and ARSC
Every resource in an Android application is assigned a unique 32-bit integer ID. These IDs are typically defined in the R.java file (for Java) or directly in Kotlin/other languages, allowing developers to reference resources programmatically. The resources.arsc file contains all the metadata for these resources: package names, resource types (e.g., layout, string, id), resource names, and their associated values across different configurations (e.g., languages, screen densities). When public.xml is missing, Apktool relies on internal heuristics or its own default mappings, which often fall short, especially for custom or obfuscated resources. The goal of reconstruction is to regenerate this public.xml file, providing Apktool with the necessary explicit mappings.
The Role of public.xml
The public.xml file, typically located in res/values/public.xml within the APK’s decompiled structure, explicitly declares public resource IDs. It looks something like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<public type="drawable" name="ic_launcher_background" id="0x7f070000" />
<public type="mipmap" name="ic_launcher_round" id="0x7f090001" />
<public type="string" name="app_name" id="0x7f110000" />
<public type="layout" name="activity_main" id="0x7f0b0000" />
</resources>
Each <public> tag maps a resource type and name to its hexadecimal id. This file is critical for tools like Apktool to correctly resolve resource references during decompilation and subsequent recompilation.
Manual Reconstruction Challenges
Manually reconstructing public.xml for a large application is a monumental, if not impossible, task. It would involve:
- Extracting
resources.arsc. - Using tools like
aapt2 dump resources <apk_path> --valuesto get a verbose listing of all resources. - Manually parsing this lengthy output to identify resource IDs, types, and names.
- Handcrafting the XML structure for each entry.
This process is error-prone, time-consuming, and simply not feasible for batch processing or frequent analysis.
Automating with Python and AAPT2
The core idea behind automation is to programmatically extract the resource information from an APK using a reliable tool and then generate the public.xml file. AAPT2 (Android Asset Packaging Tool 2), which is part of the Android SDK Build Tools, is an excellent candidate for this. Specifically, the aapt2 dump resources --values command provides a comprehensive listing of all resources, including their IDs, types, and names. This output can be parsed to reconstruct public.xml.
Step-by-Step Python Automation Logic
- Locate APKs: The script needs to find target APK files.
- Execute AAPT2: For each APK, run
aapt2 dump resources <apk_path> --values. - Parse Output: Process the textual output from AAPT2 to extract the necessary resource details (package, type, name, ID).
- Generate
public.xml: Construct the XML content based on the parsed data. - Integrate with Apktool: Place the generated
public.xmlinto the decompiled resources directory before rebuilding the APK, or use it during the initial decompilation if Apktool supports providing externalpublic.xml. For simplicity, we’ll focus on generating the file to be used in a subsequent Apktool rebuild.
Python Script Example
Below is a Python script that orchestrates this process. It assumes aapt2 is in your system’s PATH or you provide its full path.
import subprocess
import os
import re
from xml.etree import ElementTree as ET
def run_aapt2_dump(apk_path):
"""Runs aapt2 dump resources --values on the given APK and returns the output."""
try:
# Ensure aapt2 is accessible in PATH or provide full path
command = ["aapt2", "dump", "resources", apk_path, "--values"]
result = subprocess.run(command, capture_output=True, text=True, check=True)
return result.stdout
except subprocess.CalledProcessError as e:
print(f"Error running aapt2 on {apk_path}: {e}")
print(f"Stderr: {e.stderr}")
return None
def parse_aapt2_output(aapt2_output):
"""Parses aapt2 dump output to extract resource details."""
resources = []
# Regex to capture type, package, name, and ID
# Example line: "resource 0x7f010000 com.example.app:attr/MyCustomAttr: flags=0x00000000" (simplified)
# More robust regex for --values output:
# Matches lines like: " resource 0x7f030000 app:layout/activity_main: type=layout(0x03) ..."
resource_pattern = re.compile(r"resource (0x[0-9a-fA-F]+)s+[^:]+:(.+)/([a-zA-Z0-9_.]+):.*")
for line in aapt2_output.splitlines():
match = resource_pattern.search(line.strip())
if match:
res_id = match.group(1)
res_type = match.group(2).strip() # e.g., 'layout', 'string'
res_name = match.group(3).strip() # e.g., 'activity_main', 'app_name'
resources.append({'id': res_id, 'type': res_type, 'name': res_name})
return resources
def generate_public_xml(resources, output_path):
"""Generates a public.xml file from extracted resource details."""
root = ET.Element("resources")
for res in resources:
public_elem = ET.SubElement(root, "public")
public_elem.set("type", res['type'])
public_elem.set("name", res['name'])
public_elem.set("id", res['id'])
tree = ET.ElementTree(root)
# Pretty print XML
ET.indent(tree, space=" ", level=0)
tree.write(output_path, encoding="utf-8", xml_declaration=True)
print(f"Generated public.xml at {output_path}")
def reconstruct_apk_resources(apk_path, output_dir):
"""Main function to reconstruct resources for a given APK."""
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"Processing APK: {apk_path}")
aapt2_output = run_aapt2_dump(apk_path)
if aapt2_output:
parsed_resources = parse_aapt2_output(aapt2_output)
if parsed_resources:
apk_name = os.path.splitext(os.path.basename(apk_path))[0]
public_xml_path = os.path.join(output_dir, f"{apk_name}_public.xml")
generate_public_xml(parsed_resources, public_xml_path)
print(f"Successfully processed {apk_name}")
else:
print(f"No resources parsed from {apk_path}.")
else:
print(f"Failed to get aapt2 output for {apk_path}.")
if __name__ == "__main__":
# Example usage:
# For a single APK:
# reconstruct_apk_resources("path/to/your/app.apk", "./reconstructed_xmls")
# For batch processing APKs in a directory:
apk_directory = "./apks_to_process"
output_directory = "./reconstructed_xmls"
if not os.path.exists(apk_directory):
print(f"Error: APK directory '{apk_directory}' not found.")
exit(1)
for filename in os.listdir(apk_directory):
if filename.endswith(".apk"):
full_apk_path = os.path.join(apk_directory, filename)
reconstruct_apk_resources(full_apk_path, output_directory)
print("nBatch reconstruction complete.")
How to Use the Script
- Install AAPT2: Ensure
aapt2is installed and accessible in your system’s PATH. It’s usually found in<ANDROID_SDK_ROOT>/build-tools/<version>/aapt2. - Save the Script: Save the code above as a Python file (e.g.,
reconstruct_arsc.py). - Prepare APKs: Place the APKs you want to process into a directory (e.g.,
./apks_to_process). - Run the Script:
python reconstruct_arsc.py
The script will generate separate _public.xml files for each APK in the specified output directory.
Integrating with Apktool for Decompilation and Recompilation
Once you have the generated public.xml, you can integrate it into your Apktool workflow:
- Decompile the APK (normally):
apktool d your_app.apk -o your_app_decompiled - Replace/Add
public.xml: Copy the generated<apk_name>_public.xmlfile into theyour_app_decompiled/res/values/directory, renaming it topublic.xml. Overwrite if one exists (though typically, if you’re doing this, the existing one is insufficient). - Recompile the APK:
apktool b your_app_decompiled -o your_app_recompiled.apk - Sign the Recompiled APK: Use
apksignerorjarsignerto sign the newly built APK for installation.
By following these steps, Apktool will utilize the comprehensive public.xml you provided, leading to much cleaner and more accurate resource decompilation, making further analysis significantly easier.
Considerations and Limitations
- AAPT2 Version: The output format of
aapt2 dump resourcescan vary slightly between different versions of AAPT2. The regex in the script might need minor adjustments for extremely old or new, incompatible versions. - Obfuscated Resource Names: If resource names themselves are heavily obfuscated (e.g., renamed to single letters by ProGuard), this script will restore the original (obfuscated) names, not necessarily human-readable ones. Further static analysis would be required to infer their original purpose.
- Resource Overlays: This approach primarily focuses on the base APK’s resources. For complex applications using resource overlays, additional logic might be needed to handle overlay resource tables.
- Error Handling: The provided script includes basic error handling for
subprocesscalls but could be extended for more robust file I/O and parsing error management.
Conclusion
Automating the reconstruction of Android resources from the resources.arsc file significantly enhances the efficiency and accuracy of Android application reverse engineering. By leveraging Python’s scripting capabilities with the power of AAPT2, security researchers and developers can overcome challenges posed by missing public.xml files, leading to clearer decompiled resources and a more productive analysis workflow. This expert-level approach transforms a tedious manual task into a streamlined, repeatable process essential for deep dives into Android binaries.
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →