Crates.io | abxml |
lib.rs | abxml |
version | 0.8.2 |
source | src |
created_at | 2017-03-25 10:41:10.681955 |
updated_at | 2019-11-03 11:12:08.450798 |
description | Parser for resources.arsc contained on APKs and binary XML decompresser. |
homepage | https://superanalyzer.rocks/ |
repository | https://github.com/SUPERAndroidAnalyzer/abxml-rs/ |
max_upload_size | |
id | 9145 |
size | 31,686,168 |
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.
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.
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).
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.
To prepare the test environment the following tools should be installed on your path:
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.