18 releases

0.8.2 Nov 3, 2019
0.7.4 Oct 23, 2019
0.7.3 Jun 1, 2019
0.7.2 Jan 2, 2019
0.1.0 Mar 25, 2017

#2492 in Parser implementations

40 downloads per month

MIT/Apache

3.5MB
5K SLoC

Coverage Status Build Status

ABXML

ABXML is a library that is able to decode APK files in Rust. It tries to make easy to work with the binary documents found inside the APK files. It is able to decode the contained resources.arsc and also all the binary XML documents contained on the res/ folder. It also exposes some structures to work at a lower level, in case anyone will be interested.

The code is deeply inspired on Apktool: Without it, this library wouldn't exist.

Quick example

The easiest way to use the library is using the helper struct APK to decompress and decode it to the filesystem.

use std::path::Path;
use abxml::apk::Apk;

fn main() {
    let path = Path::new("path_to.apk");
    let mut apk = Apk::new(path).unwrap();
    apk.export(Path::new("/tmp/apk_output/"), false).unwrap();
}

The Apk::new will create a handler that will allow to export to the filesystem. At this moment, it will load to memory the APK, decompress it and parse the contained resources.arsc. If this process succeeds, using the method export, it will start exporting all the contained files. If it finds an Android binary XML, it will convert it to a string version of it; otherwise, it will move it to the filesystem as is. The second parameter on the export function is used to force the removal of the path given on the first argument. In this case, the second invocation of this snippet will fail, as the directory will be non empty.

Visitors

This library uses the visitor pattern to access to the contents of a binary file. There is a helper struct called Executor which is in charge of, given the contents of one binary file, call to the corresponding functions on the given visitor. The next example will print to the output the message for each string table found:

use abxml::visitor::{Executor, ChunkVisitor};

pub struct PrintVisitor;

impl<'a> ChunkVisitor<'a> for PrintVisitor {
    fn visit_string_table(&mut self, string_table: StringTableWrapper<'a>, origin: Origin) {
        println!("Found string table with origin: {:?}", origin);
    }
}

fn main() {
    let data = [];
    let mut visitor = PrintVisitor;

    Executor::xml(&data, &mut visitor);
}

Executor contains two public methods that should be used depending on the type of the input: arsc to decode resources.arsc and xml for binary XMLs. The reason of this split is because the header of the files is distinct (resources.arsc has a 12 bytes header, while binary XMLs has 8 bytes).

Wrapper, Buffers and traits

On the model namespace there are several traits that exposes how the library works with each one of the concepts behind the binary files. Each of the traits are usually implemented by both wrapper and buffers. Why this distinction?

Wrappers gives a read-only view of the represented chunk. This way, the library only allocates data when is accessed. On the other hand, the Buf structs are owned and intended to be used as mutable.

All wrappers have functions to be converted to buffers (to_buffer) and all buffers have functions to create an encoded view as bytes (through the OwnedBuf trait). This means that it's possible to do bidirectional conversions.

Comparing to Apktool

To prepare the test environment the following tools should be installed on your path:

  1. Rust
  2. Apktool
  3. xmllint
  4. colordiff

After that, you should be able to run:

./script/compare_apk.sh PATH_TO_APK

You should see, per each file, if the output of the library matches with the output of Apktool. In case that there is some difference, it will print the diff of the outputs.

Dependencies

~5–14MB
~149K SLoC