Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: high level api #1

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@
- [ ] Parallelize serialization/deserialization via `rayon`
- [ ] Use `rayon` feature in `dashmap`
- [ ] Implement `dex` file writer
- [ ] Fix `TryIntoCtx` traits to use `&mut Vec<u8>` instead of `&mut [u8]`
- [ ] Fix `TryIntoCtx` traits to use `&mut Vec<u8>` instead of `&mut [u8]`
- [ ] Add `smali!` macro
- [ ] Smali parsing via [`tree-sitter-smali`](https://crates.io/crates/tree-sitter-smali)
- [ ] VSCode extension for syntax highlighting using [Smalise](https://github.com/LoyieKing/Smalise)
77 changes: 77 additions & 0 deletions src/dex/annotations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use super::{strings::DexString, DexFile};
use crate::raw::{
annotations::{Annotation as RawAnnotation, Visibility},
encoded_value::{AnnotationElement as RawAnnotationElement, EncodedValue},
};

// TODO: write traits or use once_cells here

#[derive(derivative::Derivative)]
#[derivative(Debug)]
pub struct Annotation<'a> {
#[derivative(Debug = "ignore")]
dex: &'a DexFile<'a>,
type_idx: u32,
visibility: Visibility,
elements: Vec<AnnotationElement<'a>>,
}

impl<'a> Annotation<'a> {
pub fn new(dex: &'a DexFile<'a>, raw: RawAnnotation) -> Self {
Self {
dex,
type_idx: raw.annotation.type_idx as u32,
visibility: raw.visibility,
elements: raw
.annotation
.elements
.into_iter()
.map(|e| AnnotationElement::new(dex, e))
.collect(),
}
}

pub fn descriptor(&self) -> crate::Result<DexString> {
let id = self.dex.strings().id_at_type_idx(self.type_idx)?;
Ok(self.dex.strings().get(&id)?)
}

pub fn visibility(&self) -> Visibility {
self.visibility
}

pub fn elements(&self) -> &[AnnotationElement<'a>] {
&self.elements
}
}

#[derive(derivative::Derivative)]
#[derivative(Debug)]
pub struct AnnotationElement<'a> {
#[derivative(Debug = "ignore")]
dex: &'a DexFile<'a>,
name_idx: u64,
value: EncodedValue, // TODO: use high level EncodedValue
}

impl<'a> AnnotationElement<'a> {
pub fn new(dex: &'a DexFile<'a>, raw: RawAnnotationElement) -> Self {
Self {
dex,
name_idx: raw.name_idx,
value: raw.value,
}
}

pub fn name(&self) -> crate::Result<DexString> {
let id = self
.dex
.strings()
.id_at_idx(self.name_idx.try_into().expect("bad name idx?"))?;
Ok(self.dex.strings().get(&id)?)
}

pub fn value(&self) -> &EncodedValue {
&self.value
}
}
239 changes: 239 additions & 0 deletions src/dex/classes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
use std::sync::Arc;

use super::{
annotations::Annotation, fields::DexField, methods::DexMethod, strings::DexString, traits,
DexFile,
};
use crate::{
raw::{
annotations::{AnnotationSetItem, AnnotationsDirectory},
class_data::{ClassData, EncodedMethod},
classdef::ClassDef,
encoded_value::{EncodedArrayItem, EncodedValueError},
flags::AccessFlags,
string::StringId,
type_list::TypeList,
NO_INDEX, NO_OFFSET,
},
utils::IntoArc,
Result,
};
use once_cell::unsync::OnceCell;
use scroll::Pread;

pub mod iter;

#[derive(derivative::Derivative)]
#[derivative(Debug)]
pub struct DexClass<'a> {
#[derivative(Debug = "ignore")]
dex: &'a DexFile<'a>,
def: ClassDef,
data: Option<ClassData>, // TODO: lazy load

// internal
descriptor_id: OnceCell<StringId>,
annotations_dir: OnceCell<Option<Arc<AnnotationsDirectory>>>,
static_values: OnceCell<Option<EncodedArrayItem>>,
}

impl<'a> DexClass<'a> {
pub fn new(dex: &'a DexFile<'a>, def: ClassDef) -> Result<Self> {
let data = if def.class_data_off > 0 {
Some(dex.src.pread(def.class_data_off as usize)?)
} else {
None
};
Ok(Self {
dex,
def,
data,

// internal
descriptor_id: OnceCell::new(),
annotations_dir: OnceCell::new(),
static_values: OnceCell::new(),
})
}
}

impl<'a> traits::Class for DexClass<'a> {
fn descriptor(&self) -> Result<DexString> {
let id = self
.descriptor_id
.get_or_try_init(|| self.dex.strings().id_at_type_idx(self.def.class_idx))?;
Ok(self.dex.strings().get(id)?)
}

fn superclass(&self) -> Result<Option<DexString>> {
if self.def.superclass_idx == NO_INDEX {
return Ok(None);
}
let id = self.dex.strings().id_at_type_idx(self.def.superclass_idx)?;
Ok(Some(self.dex.strings().get(&id)?))
}

fn access_flags(&self) -> &AccessFlags {
&self.def.access_flags
}

fn source_file(&self) -> Result<Option<DexString>> {
if self.def.source_file_idx == NO_INDEX {
return Ok(None);
}
let id = self.dex.strings().id_at_idx(self.def.source_file_idx)?;
Ok(Some(self.dex.strings().get(&id)?))
}

fn interfaces(&self) -> Result<Vec<DexString>> {
let mut interfaces = Vec::new();
if self.def.interfaces_off == 0 {
return Ok(interfaces);
}
let ty_list = self
.dex
.src
.pread_with::<TypeList>(self.def.interfaces_off as usize, scroll::LE)?
.into_inner();
interfaces.reserve(ty_list.len());
for ty in ty_list {
let id = self.dex.strings().id_at_type_idx(ty.type_idx as u32)?;
interfaces.push(self.dex.strings().get(&id)?);
}
Ok(interfaces)
}

fn annotations(&self) -> Result<Vec<Annotation>> {
let mut annotations = Vec::new();
let offset = match self.annotations_dir()? {
Some(dir) if dir.class_annotations_off != NO_OFFSET => dir.class_annotations_off,
_ => return Ok(annotations),
};
let offsets = self
.dex
.src
.pread_with::<AnnotationSetItem>(offset as usize, scroll::LE)?
.into_inner();
annotations.reserve(offsets.len());
for offset in offsets {
let raw = self.dex.src.pread_with(offset as usize, scroll::LE)?;
annotations.push(Annotation::new(self.dex, raw));
}
Ok(annotations)
}

fn static_fields(&self) -> Result<Vec<DexField<'a>>> {
let mut fields = Vec::new();
let efs = match &self.data {
Some(data) => &data.static_fields,
_ => return Ok(Vec::new()),
};
let mut static_values = self.static_values()?.map(|v| v.iter());
let annotations_dir = self.annotations_dir()?;
let mut prev_idx = 0;
for ef in efs {
let initial_value = match static_values {
Some(ref mut values) => values.next(),
_ => None,
};
let field = DexField::new(
self.dex,
self,
ef,
prev_idx,
initial_value.cloned(),
annotations_dir.clone(),
)?;
prev_idx = field.idx;
fields.push(field);
}
Ok(fields)
}

fn instance_fields(&self) -> Result<Vec<DexField<'a>>> {
let mut fields = Vec::new();
let efs = match &self.data {
Some(data) => &data.instance_fields,
_ => return Ok(Vec::new()),
};
let annotations_dir = self.annotations_dir()?;
let mut prev_idx = 0;
for ef in efs {
let field = DexField::new(self.dex, self, ef, prev_idx, None, annotations_dir.clone())?;
prev_idx = field.idx;
fields.push(field);
}
Ok(fields)
}

fn fields(&self) -> Result<Vec<DexField<'a>>> {
let mut fields = Vec::new();
fields.extend(self.static_fields()?);
fields.extend(self.instance_fields()?);
Ok(fields)
}

fn direct_methods(&self) -> Result<Vec<DexMethod<'a>>> {
self.parse_methods(match &self.data {
Some(data) => &data.direct_methods,
_ => return Ok(Vec::new()),
})
}

fn virtual_methods(&self) -> Result<Vec<DexMethod<'a>>> {
self.parse_methods(match &self.data {
Some(data) => &data.virtual_methods,
_ => return Ok(Vec::new()),
})
}

fn methods(&self) -> Result<Vec<DexMethod<'a>>> {
let mut methods = Vec::new();
methods.extend(self.direct_methods()?);
methods.extend(self.virtual_methods()?);
Ok(methods)
}
}

impl<'a> DexClass<'a> {
fn annotations_dir(&self) -> Result<Option<Arc<AnnotationsDirectory>>> {
Ok(self
.annotations_dir
.get_or_try_init(|| {
if self.def.annotations_off == NO_OFFSET {
return Ok::<_, scroll::Error>(None);
}
let dir: AnnotationsDirectory = self
.dex
.src
.pread_with(self.def.annotations_off as usize, scroll::LE)?;
Ok(Some(dir.into_arc()))
})?
.clone())
}

fn static_values(&self) -> Result<Option<&EncodedArrayItem>> {
Ok(self
.static_values
.get_or_try_init(|| {
if self.def.static_values_off == NO_OFFSET {
return Ok::<_, EncodedValueError>(None);
}
let item = self.dex.src.pread(self.def.static_values_off as usize)?;
Ok(Some(item))
})?
.as_ref())
}

fn parse_methods(&self, raw_methods: &[EncodedMethod]) -> Result<Vec<DexMethod<'a>>> {
let mut methods = Vec::new();
let annotations_dir = self.annotations_dir()?;
let mut prev_idx = 0;
for raw in raw_methods {
let method = DexMethod::new(self.dex, self, raw, prev_idx, annotations_dir.clone())?;
prev_idx = method.idx;
methods.push(method);
}
Ok(methods)
}
}
42 changes: 42 additions & 0 deletions src/dex/classes/iter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use super::DexClass;
use crate::dex::DexFile;

pub struct ClassIterator<'a> {
dex: &'a DexFile<'a>,
index: usize,
count: usize,
}

impl<'a> ClassIterator<'a> {
pub fn new(dex: &'a DexFile<'a>, count: usize) -> Self {
Self {
dex,
index: 0,
count,
}
}

pub fn size(&self) -> usize {
self.count
}
}

impl<'a> Iterator for ClassIterator<'a> {
type Item = crate::Result<DexClass<'a>>;

fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.count {
return None;
}
let class_def = match self.dex.class_def(self.index) {
Ok(class_def) => class_def,
Err(err) => return Some(Err(err)),
};
let class = match DexClass::new(self.dex, class_def) {
Ok(class) => class,
Err(err) => return Some(Err(err)),
};
self.index += 1;
Some(Ok(class))
}
}
Loading