resand 0.3.0

Read and write ARSC and AXML binary files used for Android Resources
Documentation
  • Coverage
  • 20.3%
    134 out of 660 items documented0 out of 356 items with examples
  • Size
  • Source code size: 324.62 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 7.67 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 7s Average build duration of successful builds.
  • all releases: 15s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Repository
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • fieryhenry

Resand

Resand (RESesource ANDroid) is a library for android resource reading and writing. It can read and write AXML (ResXML) and ARSC (ResTable) files.

The purpose of this crate is that I wanted to be able to change the app name and package name of an apk without including a large dependency such as apktool, or aapt, or any other similar external tool.

At the moment it cannot change resource data into XML and XML into resource data, but I am working on adding that, so at the moment all modifications to resource data have to be done directly in Rust.

There is not much documentation yet as this was mainly something I needed for one of my other projects.

The data structures in the code are based off of the Android Source Code

Usage

cargo add resand

The following examples use .unwrap(), but obviously you should handle errors properly.

Basic reading and writing of and xml tree:

use resand::xmltree::XMLTree;
use std::fs::File;

let mut file = File::open("./AndroidManifest.xml").unwrap();
let tree = XMLTree::read(&mut file).unwrap();

dbg!(&tree);

let mut output = File::create("./NewAndroidManifest.xml").unwrap();

tree.write(&mut output).unwrap();

Basic reading and writing of a resource table:

use resand::table::ResTable
use std::fs::File;

let mut file = File::open("./resources.arsc").unwrap();
let table = ResTable::read_all(&mut file).unwrap();

dbg!(&table);

let mut output = File::create("./new_resources.arsc").unwrap();

table.write_all(&mut output).unwrap();

Setting the app name:

let xml = XMLTree::read(...);
let mut table = ResTable::read_all(...)

let application = xml
    .root
    .get_elements(&["manifest", "application"], &xml.string_pool)
    .pop()
    .unwrap();

let attr = application
    .get_attribute("label", &xml.string_pool).unwrap();

if let ResValueType::Reference(attr) = attr.typed_value.data {
    let package = table.packages.first_mut().unwrap();
    let entry = package
        .resolve_ref_mut(attr).unwrap();
    if let ResTableEntryValue::ResValue(value) = &mut entry.data {
        value
            .data
            .write_string("New Name".to_string(), &mut table.string_pool);
    }
}

table.write_all(...)

Setting the package name:


let mut xml = (...)
let mut table = (...)

let package = table.packages.first_mut().unwrap();
package.name = "new.package.name".to_string();

let root_element = xml
    .root
    .get_elements_mut(&["manifest"], &xml.string_pool)
    .pop().unwrap();

let attr = root_element
    .get_attribute_mut("package", &xml.string_pool).unwrap();

attr.write_string("new.package.name".to_string(), &mut xml.string_pool);

// you might need to modify other parts of the manifest if they also refer to the package name

xml.write(...);
table.write_all(...);

Using the derive feature

cargo add resand --features derive

Setting the package name

use std::path::Path;

use resand::{
    manifest::AndroidManifest,
    traits::{FromNode, IntoNode},
    xmltree::XMLTree,
};

fn main() {
    let tree = XMLTree::from_path(Path::new(
        "path/to/AndroidManifest.xml",
    ))
    .unwrap();
    let mut manifest = AndroidManifest::from_tree(tree).unwrap();

    manifest.package = "com.foo.bar".to_string();

    let mut output_buf = std::io::Cursor::new(Vec::new());

    let tree = manifest.into_tree();

    tree.write(&mut output_buf).unwrap();
}

Creating a new binary XML file

Target:

<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="user" />
        </trust-anchors>
    </debug-overrides>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </base-config>
</network-security-config>

Code:

fn main() {
use resand::traits::IntoNode;
use resand_derive::{FromNode, IntoNode};

#[derive(Debug, IntoNode, FromNode)]
pub struct Certificate {
    #[resand(attr)]
    pub src: String,
}

#[derive(Debug, IntoNode, FromNode)]
pub struct TrustAnchor {
    #[resand(child, many)]
    pub certificates: Vec<Certificate>,
}

#[derive(Debug, IntoNode, FromNode)]
pub struct DebugOverride {
    #[resand(child, name = "trust-anchors")] // you can rename stuff like this
    pub trust_anchors: TrustAnchor,
}

#[derive(Debug, IntoNode, FromNode)]
pub struct BaseConfig {
    #[resand(attr)]
    pub cleartext_traffic_permitted: bool,
    #[resand(child)]
    pub trust_anchors: TrustAnchor,
}

#[derive(Debug, IntoNode, FromNode)]
#[resand(name = "network-security-config")]
pub struct NetworkSecurityConfig {
    #[resand(child)]
    pub debug_overrides: DebugOverride,
    #[resand(child)]
    pub base_config: BaseConfig,
}

fn main() {
    let tree = NetworkSecurityConfig {
        debug_overrides: DebugOverride {
            trust_anchors: TrustAnchor {
                certificates: vec![Certificate {
                    src: "user".to_string(),
                }],
            },
        },
        base_config: BaseConfig {
            cleartext_traffic_permitted: true,
            trust_anchors: TrustAnchor {
                certificates: vec![
                    Certificate {
                        src: "system".to_string(),
                    },
                    Certificate {
                        src: "user".to_string(),
                    },
                ],
            },
        },
    }
    .into_tree();

    let mut file = std::fs::File::create("path/to/network_security_config.xml").unwrap();
    tree.write(&mut file).unwrap();
}

License

Resand is licensed under the Apache-2.0 license which can be read here