pub mod parser;
pub mod chunks;
pub mod errors;
use std::{
fs,
collections::HashMap,
path::Path,
};
use std::io::{
Read,
Cursor,
};
use crate::errors::AxmlError;
use crate::chunks::{
resource_map::ResourceMap,
res_table::ResTable,
string_pool::StringPool,
};
use crate::parser::{
Axml,
XmlNode
};
#[derive(Debug, PartialEq)]
pub enum ComponentState {
Unknown,
DefaultTrue,
DefaultFalse,
ExplicitTrue,
ExplicitFalse,
}
pub fn parse_from_apk<P: AsRef<Path>>(file_path: P) -> Result<Axml, AxmlError> {
let cursor = create_cursor_from_apk(file_path)?;
parse_from_cursor(cursor)
}
pub fn parse_from_axml<P: AsRef<Path>>(file_path: P) -> Result<Axml, AxmlError> {
let cursor = create_cursor_from_axml(file_path)?;
parse_from_cursor(cursor)
}
pub fn create_cursor_from_apk<P: AsRef<Path>>(file_path: P) -> Result<Cursor<Vec<u8>>, AxmlError> {
let mut axml_cursor = Vec::new();
let zipfile = std::fs::File::open(file_path.as_ref())?;
let mut archive = zip::ZipArchive::new(zipfile)?;
let mut raw_file = match archive.by_name("AndroidManifest.xml") {
Ok(file) => file,
Err(..) => {
panic!("Error: no AndroidManifest.xml in APK");
}
};
raw_file.read_to_end(&mut axml_cursor)?;
Ok(Cursor::new(axml_cursor))
}
pub fn create_cursor_from_axml<P: AsRef<Path>>(file_path: P) -> Result<Cursor<Vec<u8>>, AxmlError> {
let mut axml_cursor = Vec::new();
let mut raw_file = fs::File::open(file_path.as_ref())?;
raw_file.read_to_end(&mut axml_cursor)?;
Ok(Cursor::new(axml_cursor))
}
pub fn parse_from_cursor(axml_cursor: Cursor<Vec<u8>>) -> Result<Axml, AxmlError> {
parser::parse_xml(axml_cursor)
}
pub fn parse_from_string(axml_str: &str) -> Result<Axml, AxmlError> {
let axml_cursor = Cursor::new(Vec::from(axml_str.as_bytes()));
parser::parse_xml(axml_cursor)
}
pub fn parse_from_reader<R>(mut reader: R) -> Result<Axml, AxmlError>
where
R: Read,
{
let mut axml_vec = Vec::<u8>::new();
reader.read_to_end(&mut axml_vec)?;
let axml_cursor = Cursor::new(axml_vec);
parser::parse_xml(axml_cursor)
}
pub fn find_nodes_by_type(axml: &Axml, element_type: &str) -> Vec<XmlNode> {
axml.iter()
.filter(|element| element.borrow().element_type() == element_type)
.collect()
}
pub fn find_node_by_name(axml: &Axml, node_name: &str) -> Option<XmlNode> {
let name = node_name.to_string();
axml.iter()
.find(|element| element.borrow().get_name() == Some(&name))
}
pub fn is_component_exposed(component: &XmlNode) -> bool {
let mut _enabled_state = ComponentState::DefaultTrue;
let mut exported_state = ComponentState::Unknown;
if let Some(enabled) = component.borrow().get_attr("android:enabled") {
if enabled == "false" {
return false;
} else {
_enabled_state = ComponentState::ExplicitTrue;
}
}
if let Some(exported) = component.borrow().get_attr("android:exported") {
if exported == "false" {
return false;
} else {
exported_state = ComponentState::ExplicitTrue;
}
}
if exported_state == ComponentState::Unknown {
for item in component.borrow().children().iter() {
if item.borrow().element_type() == "intent-filter" {
exported_state = ComponentState::DefaultTrue;
break;
}
}
if exported_state == ComponentState::Unknown {
exported_state = ComponentState::DefaultFalse;
}
}
match exported_state {
ComponentState::DefaultFalse => false,
ComponentState::DefaultTrue => true,
ComponentState::ExplicitTrue => true,
_ => panic!("never going to happen")
}
}
pub fn get_activities_names(parsed_xml: &Axml) -> Vec<String> {
find_nodes_by_type(parsed_xml, "activity")
.into_iter()
.filter(|element| element.borrow().get_name().is_some())
.map(|element| element.borrow().get_name().unwrap().to_string())
.collect()
}
pub fn get_services_names(parsed_xml: &Axml) -> Vec<String> {
find_nodes_by_type(parsed_xml, "service")
.into_iter()
.filter(|element| element.borrow().get_name().is_some())
.map(|element| element.borrow().get_name().unwrap().to_string())
.collect()
}
pub fn get_providers_names(parsed_xml: &Axml) -> Vec<String> {
find_nodes_by_type(parsed_xml, "provider")
.into_iter()
.filter(|element| element.borrow().get_name().is_some())
.map(|element| element.borrow().get_name().unwrap().to_string())
.collect()
}
pub fn get_receivers_names(parsed_xml: &Axml) -> Vec<String> {
find_nodes_by_type(parsed_xml, "receiver")
.into_iter()
.filter(|element| element.borrow().get_name().is_some())
.map(|element| element.borrow().get_name().unwrap().to_string())
.collect()
}
pub fn get_declared_permissions(parsed_xml: &Axml) -> Vec<String> {
find_nodes_by_type(parsed_xml, "permission")
.into_iter()
.filter(|element| element.borrow().get_name().is_some())
.map(|element| element.borrow().get_name().unwrap().to_string())
.collect()
}
pub fn get_requested_permissions(parsed_xml: &Axml) -> Vec<String> {
find_nodes_by_type(parsed_xml, "uses-permission")
.into_iter()
.filter(|element| element.borrow().get_name().is_some())
.map(|element| element.borrow().get_name().unwrap().to_string())
.collect()
}
pub fn get_exposed_components(parsed_xml: &Axml) -> Option<HashMap<String, Vec<XmlNode>>> {
let application = find_nodes_by_type(parsed_xml, "application").pop()?;
if let Some(enabled) = application.borrow().get_attr("android:enabled") {
if enabled == "false" {
return None;
}
}
let mut components = HashMap::new();
components.insert(
String::from("activity"),
find_nodes_by_type(parsed_xml, "activity")
.into_iter()
.filter(is_component_exposed)
.collect()
);
components.insert(
String::from("service"),
find_nodes_by_type(parsed_xml, "service")
.into_iter()
.filter(is_component_exposed)
.collect()
);
components.insert(
String::from("provider"),
find_nodes_by_type(parsed_xml, "provider")
.into_iter()
.filter(is_component_exposed)
.collect()
);
components.insert(
String::from("receiver"),
find_nodes_by_type(parsed_xml, "receiver")
.into_iter()
.filter(is_component_exposed)
.collect()
);
Some(components)
}