update rust edition of androscalpel
This commit is contained in:
parent
d4ccc73362
commit
218d6bf6fc
20 changed files with 223 additions and 167 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "androscalpel"
|
name = "androscalpel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
[lib]
|
[lib]
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ use pyo3::prelude::*;
|
||||||
|
|
||||||
use crate::hashmap_vectorize;
|
use crate::hashmap_vectorize;
|
||||||
use crate::{
|
use crate::{
|
||||||
dex_id::IdType, value::DexValue, DexString, IdField, IdMethod, IdMethodType, MethodHandle,
|
DexString, IdField, IdMethod, IdMethodType, MethodHandle, Result, Visitable, VisitableMut,
|
||||||
Result, Visitable, VisitableMut, Visitor, VisitorMut,
|
Visitor, VisitorMut, dex_id::IdType, value::DexValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Annotation with a visibility
|
/// Annotation with a visibility
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! Representation of an apk.
|
//! Representation of an apk.
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Context};
|
use anyhow::{Context, anyhow, bail};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|
@ -11,9 +11,9 @@ use log::{error, info};
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::{prelude::*, types::PyBytes};
|
use pyo3::{prelude::*, types::PyBytes};
|
||||||
|
|
||||||
|
use crate::Result;
|
||||||
use crate::ins::CallSite;
|
use crate::ins::CallSite;
|
||||||
use crate::instructions;
|
use crate::instructions;
|
||||||
use crate::Result;
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use androscalpel_serializer::Instruction as InsFormat;
|
use androscalpel_serializer::Instruction as InsFormat;
|
||||||
use androscalpel_serializer::*;
|
use androscalpel_serializer::*;
|
||||||
|
|
@ -1030,7 +1030,9 @@ impl Apk {
|
||||||
Format11X { op: 0x27, va } => Instruction::Throw { reg: va },
|
Format11X { op: 0x27, va } => Instruction::Throw { reg: va },
|
||||||
Format10T { op: 0x28, a } => {
|
Format10T { op: 0x28, a } => {
|
||||||
if a < 0 && (-(a as i64)) as usize > addr {
|
if a < 0 && (-(a as i64)) as usize > addr {
|
||||||
bail!("Found goto {a} at 0x{addr:0x}: the destination is invalid (negative addresse)");
|
bail!(
|
||||||
|
"Found goto {a} at 0x{addr:0x}: the destination is invalid (negative addresse)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let dest_addr = if a > 0 {
|
let dest_addr = if a > 0 {
|
||||||
addr + a as usize
|
addr + a as usize
|
||||||
|
|
@ -1043,7 +1045,9 @@ impl Apk {
|
||||||
}
|
}
|
||||||
Format20T { op: 0x29, a } => {
|
Format20T { op: 0x29, a } => {
|
||||||
if a < 0 && (-(a as i64)) as usize > addr {
|
if a < 0 && (-(a as i64)) as usize > addr {
|
||||||
bail!("Found goto {a} at 0x{addr:0x}: the destination is invalid (negative addresse)");
|
bail!(
|
||||||
|
"Found goto {a} at 0x{addr:0x}: the destination is invalid (negative addresse)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let dest_addr = if a > 0 {
|
let dest_addr = if a > 0 {
|
||||||
addr + a as usize
|
addr + a as usize
|
||||||
|
|
@ -1056,7 +1060,9 @@ impl Apk {
|
||||||
}
|
}
|
||||||
Format30T { op: 0x2a, a } => {
|
Format30T { op: 0x2a, a } => {
|
||||||
if a < 0 && (-(a as i64)) as usize > addr {
|
if a < 0 && (-(a as i64)) as usize > addr {
|
||||||
bail!("Found goto {a} at 0x{addr:0x}: the destination is invalid (negative addresse)");
|
bail!(
|
||||||
|
"Found goto {a} at 0x{addr:0x}: the destination is invalid (negative addresse)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let dest_addr = if a > 0 {
|
let dest_addr = if a > 0 {
|
||||||
addr + a as usize
|
addr + a as usize
|
||||||
|
|
@ -3053,24 +3059,29 @@ impl Apk {
|
||||||
i += 1;
|
i += 1;
|
||||||
dex_name = format!("classes{i}.dex");
|
dex_name = format!("classes{i}.dex");
|
||||||
}
|
}
|
||||||
if let Some(_version) = crate::utils::is_dex(&mut code)? {
|
match crate::utils::is_dex(&mut code)? {
|
||||||
let mut data = vec![];
|
Some(_version) => {
|
||||||
std::io::copy(&mut code, &mut data)?;
|
let mut data = vec![];
|
||||||
self.add_dex_file(&dex_name, &data, label_ins, cache)
|
std::io::copy(&mut code, &mut data)?;
|
||||||
} else if crate::utils::is_zip(&mut code)? {
|
self.add_dex_file(&dex_name, &data, label_ins, cache)
|
||||||
let mut tmp_apk = Apk::load_apk(code, label_ins, cache)?;
|
}
|
||||||
let mut j = 1;
|
_ => {
|
||||||
let mut tmp_dex_name: String = "classes.dex".into();
|
if crate::utils::is_zip(&mut code)? {
|
||||||
while let Some(dex_file) = tmp_apk.dex_files.remove(&tmp_dex_name) {
|
let mut tmp_apk = Apk::load_apk(code, label_ins, cache)?;
|
||||||
self.dex_files.insert(dex_name, dex_file);
|
let mut j = 1;
|
||||||
i += 1;
|
let mut tmp_dex_name: String = "classes.dex".into();
|
||||||
j += 1;
|
while let Some(dex_file) = tmp_apk.dex_files.remove(&tmp_dex_name) {
|
||||||
dex_name = format!("classes{i}.dex");
|
self.dex_files.insert(dex_name, dex_file);
|
||||||
tmp_dex_name = format!("classes{j}.dex");
|
i += 1;
|
||||||
|
j += 1;
|
||||||
|
dex_name = format!("classes{i}.dex");
|
||||||
|
tmp_dex_name = format!("classes{j}.dex");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
bail!("Could not recognize the type of the input file")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
bail!("Could not recognize the type of the input file")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ use std::collections::{HashMap, HashSet};
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ins::Instruction, DexString, IdField, IdMethod, IdMethodType, IdType, Method, MethodHandle,
|
DexString, IdField, IdMethod, IdMethodType, IdType, Method, MethodHandle, Result, Visitable,
|
||||||
Result, Visitable, VisitableMut, Visitor, VisitorMut,
|
VisitableMut, Visitor, VisitorMut, ins::Instruction,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: make this easy to edit/manipulate, maybe move to Method
|
// TODO: make this easy to edit/manipulate, maybe move to Method
|
||||||
|
|
|
||||||
|
|
@ -293,19 +293,24 @@ impl<'a> MethodCFG<'a> {
|
||||||
" node_{i} [shape=record,style=filled,fillcolor=lightgrey,label=\"{label}\"];\n\n"
|
" node_{i} [shape=record,style=filled,fillcolor=lightgrey,label=\"{label}\"];\n\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
dot_string +=
|
dot_string += " node_end [shape=record,style=filled,fillcolor=lightgrey,label=\"{\\< EXIT \\>}\"];\n\n";
|
||||||
" node_end [shape=record,style=filled,fillcolor=lightgrey,label=\"{\\< EXIT \\>}\"];\n\n";
|
|
||||||
|
|
||||||
for (i, node) in self.nodes.iter().enumerate() {
|
for (i, node) in self.nodes.iter().enumerate() {
|
||||||
for j in &node.next_nodes {
|
for j in &node.next_nodes {
|
||||||
if *j == i + 1 {
|
if *j == i + 1 {
|
||||||
dot_string += &format!(" node_{i}:s -> node_{j}:n [style=\"solid,bold\",color=black,weight=100,constraint=true];\n");
|
dot_string += &format!(
|
||||||
|
" node_{i}:s -> node_{j}:n [style=\"solid,bold\",color=black,weight=100,constraint=true];\n"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
dot_string += &format!(" node_{i}:s -> node_{j}:n [style=\"solid,bold\",color=black,weight=10,constraint=true];\n");
|
dot_string += &format!(
|
||||||
|
" node_{i}:s -> node_{j}:n [style=\"solid,bold\",color=black,weight=10,constraint=true];\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if node.next_nodes.is_empty() {
|
if node.next_nodes.is_empty() {
|
||||||
dot_string += &format!(" node_{i}:s -> node_end:n [style=\"solid,bold\",color=black,weight=10,constraint=true];\n");
|
dot_string += &format!(
|
||||||
|
" node_{i}:s -> node_end:n [style=\"solid,bold\",color=black,weight=10,constraint=true];\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dot_string += "}\n";
|
dot_string += "}\n";
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,9 @@ impl MethodCFG<'_> {
|
||||||
node_label += reg.to_str();
|
node_label += reg.to_str();
|
||||||
}
|
}
|
||||||
node_label += "|";
|
node_label += "|";
|
||||||
dot_string += &format!(" node_{label} [shape=record,style=filled,fillcolor=lightgrey,label=\"{node_label}\"];\n");
|
dot_string += &format!(
|
||||||
|
" node_{label} [shape=record,style=filled,fillcolor=lightgrey,label=\"{node_label}\"];\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dot_string += "}\n";
|
dot_string += "}\n";
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ use std::cmp::{Ord, Ordering, PartialOrd};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Context};
|
use anyhow::{Context, anyhow, bail};
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
use crate::{scalar::*, DexString, DexValue, Result, Visitable, VisitableMut, Visitor, VisitorMut};
|
use crate::{DexString, DexValue, Result, Visitable, VisitableMut, Visitor, VisitorMut, scalar::*};
|
||||||
use androscalpel_serializer::{StringDataItem, Uleb128};
|
use androscalpel_serializer::{StringDataItem, Uleb128};
|
||||||
|
|
||||||
/// The type of a method. The shorty is formated as described in
|
/// The type of a method. The shorty is formated as described in
|
||||||
|
|
@ -332,15 +332,18 @@ impl IdType {
|
||||||
'J' => Some(Self::long()),
|
'J' => Some(Self::long()),
|
||||||
'F' => Some(Self::float()),
|
'F' => Some(Self::float()),
|
||||||
'D' => Some(Self::double()),
|
'D' => Some(Self::double()),
|
||||||
'[' => { array_dimmention += 1; None },
|
'[' => {
|
||||||
|
array_dimmention += 1;
|
||||||
|
None
|
||||||
|
}
|
||||||
'L' => {
|
'L' => {
|
||||||
let mut class_name = String::new();
|
let mut class_name = String::new();
|
||||||
for cc in chars.by_ref(){
|
for cc in chars.by_ref() {
|
||||||
if cc == ';' { break;}
|
if cc == ';' {
|
||||||
else {
|
break;
|
||||||
|
} else {
|
||||||
class_name.push(cc);
|
class_name.push(cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Some(Self::class(&class_name))
|
Some(Self::class(&class_name))
|
||||||
}
|
}
|
||||||
|
|
@ -350,13 +353,13 @@ impl IdType {
|
||||||
};
|
};
|
||||||
if let Some(mut new_type) = new_type {
|
if let Some(mut new_type) = new_type {
|
||||||
if array_dimmention != 0 {
|
if array_dimmention != 0 {
|
||||||
let mut data = vec![0u8; array_dimmention + new_type.0 .0.data.len()];
|
let mut data = vec![0u8; array_dimmention + new_type.0.0.data.len()];
|
||||||
for c in &mut data[..array_dimmention] {
|
for c in &mut data[..array_dimmention] {
|
||||||
*c = 0x5b;
|
*c = 0x5b;
|
||||||
}
|
}
|
||||||
data[array_dimmention..].copy_from_slice(&new_type.0 .0.data);
|
data[array_dimmention..].copy_from_slice(&new_type.0.0.data);
|
||||||
new_type.0 .0.data = data;
|
new_type.0.0.data = data;
|
||||||
new_type.0 .0.utf16_size.0 += array_dimmention as u32;
|
new_type.0.0.utf16_size.0 += array_dimmention as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
lst.push(new_type);
|
lst.push(new_type);
|
||||||
|
|
@ -443,8 +446,8 @@ impl IdType {
|
||||||
#[cfg_attr(feature = "python", staticmethod)]
|
#[cfg_attr(feature = "python", staticmethod)]
|
||||||
pub fn array(type_: &IdType) -> Self {
|
pub fn array(type_: &IdType) -> Self {
|
||||||
let mut ty = type_.clone();
|
let mut ty = type_.clone();
|
||||||
ty.0 .0.utf16_size.0 += 1;
|
ty.0.0.utf16_size.0 += 1;
|
||||||
ty.0 .0.data.insert(0, 0x5b);
|
ty.0.0.data.insert(0, 0x5b);
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -644,7 +647,7 @@ impl IdType {
|
||||||
impl SmaliName for IdType {
|
impl SmaliName for IdType {
|
||||||
/// Convert a descriptor to its smali representation.
|
/// Convert a descriptor to its smali representation.
|
||||||
fn try_to_smali(&self) -> Result<String> {
|
fn try_to_smali(&self) -> Result<String> {
|
||||||
let r = (&self.0 .0).try_into()?; // Anyhow conversion stuff
|
let r = (&self.0.0).try_into()?; // Anyhow conversion stuff
|
||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
/// Convert a smali representation to its descriptor.
|
/// Convert a smali representation to its descriptor.
|
||||||
|
|
|
||||||
|
|
@ -27,27 +27,30 @@ impl<V: VisitorMut> VisitableMut<V> for DexString {
|
||||||
|
|
||||||
impl std::fmt::Debug for DexString {
|
impl std::fmt::Debug for DexString {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
if let Ok(string) = TryInto::<String>::try_into(self) {
|
match TryInto::<String>::try_into(self) {
|
||||||
f.write_str(&format!(
|
Ok(string) => {
|
||||||
"DexString({}, {:#x})",
|
f.write_str(&format!(
|
||||||
string, self.0.utf16_size.0
|
"DexString({}, {:#x})",
|
||||||
))
|
string, self.0.utf16_size.0
|
||||||
/*
|
))
|
||||||
f.debug_tuple("DexString")
|
/*
|
||||||
.field(&string)
|
f.debug_tuple("DexString")
|
||||||
.field(&self.0.utf16_size.0)
|
.field(&string)
|
||||||
.finish()
|
.field(&self.0.utf16_size.0)
|
||||||
*/
|
.finish()
|
||||||
} else {
|
*/
|
||||||
f.write_str(&format!(
|
}
|
||||||
"DexString({:?}, {:#x})",
|
_ => {
|
||||||
self.0.data, self.0.utf16_size.0
|
f.write_str(&format!(
|
||||||
))
|
"DexString({:?}, {:#x})",
|
||||||
/*f.debug_tuple("DexString")
|
self.0.data, self.0.utf16_size.0
|
||||||
.field(&self.0.data)
|
))
|
||||||
.field(&self.0.utf16_size.0)
|
/*f.debug_tuple("DexString")
|
||||||
.finish()
|
.field(&self.0.data)
|
||||||
*/
|
.field(&self.0.utf16_size.0)
|
||||||
|
.finish()
|
||||||
|
*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -193,10 +196,11 @@ impl DexString {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn __str__(&self) -> String {
|
pub fn __str__(&self) -> String {
|
||||||
if let Ok(string) = TryInto::<String>::try_into(self) {
|
match TryInto::<String>::try_into(self) {
|
||||||
string
|
Ok(string) => string,
|
||||||
} else {
|
_ => {
|
||||||
format!("string{:02x?}", self.0.data)
|
format!("string{:02x?}", self.0.data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use std::io;
|
||||||
use std::io::{Cursor, Seek, SeekFrom, Write};
|
use std::io::{Cursor, Seek, SeekFrom, Write};
|
||||||
|
|
||||||
use adler::Adler32;
|
use adler::Adler32;
|
||||||
use anyhow::{anyhow, bail, Context};
|
use anyhow::{Context, anyhow, bail};
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use sha1::{Digest, Sha1};
|
use sha1::{Digest, Sha1};
|
||||||
|
|
||||||
|
|
@ -707,7 +707,7 @@ impl DexWriter {
|
||||||
.handlers
|
.handlers
|
||||||
.push(EncodedTypeAddrPair { type_idx, addr });
|
.push(EncodedTypeAddrPair { type_idx, addr });
|
||||||
}
|
}
|
||||||
if let Some(ref label) = default_handler {
|
if let Some(label) = default_handler {
|
||||||
let catch_all_addr = *label_addrs.get(label).ok_or(anyhow!(
|
let catch_all_addr = *label_addrs.get(label).ok_or(anyhow!(
|
||||||
"Label {} not found in code of {}, but found try \
|
"Label {} not found in code of {}, but found try \
|
||||||
with this label as catch all",
|
with this label as catch all",
|
||||||
|
|
@ -1524,7 +1524,9 @@ impl DexWriter {
|
||||||
(None, Some(field)) => (false, field),
|
(None, Some(field)) => (false, field),
|
||||||
_ => bail!(
|
_ => bail!(
|
||||||
"Unexpected configuration: field {} is both a static and a instance field in {}",
|
"Unexpected configuration: field {} is both a static and a instance field in {}",
|
||||||
field_id.__repr__(), class_id.__repr__()),
|
field_id.__repr__(),
|
||||||
|
class_id.__repr__()
|
||||||
|
),
|
||||||
};
|
};
|
||||||
if !field.annotations.is_empty() {
|
if !field.annotations.is_empty() {
|
||||||
let annotations_off = self
|
let annotations_off = self
|
||||||
|
|
@ -1558,7 +1560,9 @@ impl DexWriter {
|
||||||
(None, Some(method)) => (false, method),
|
(None, Some(method)) => (false, method),
|
||||||
_ => bail!(
|
_ => bail!(
|
||||||
"Unexpected configuration: method {} is both a direct and a virtual method in {}",
|
"Unexpected configuration: method {} is both a direct and a virtual method in {}",
|
||||||
method_id.__repr__(), class_id.__repr__()),
|
method_id.__repr__(),
|
||||||
|
class_id.__repr__()
|
||||||
|
),
|
||||||
};
|
};
|
||||||
if !method.annotations.is_empty() {
|
if !method.annotations.is_empty() {
|
||||||
let annotations_off = self
|
let annotations_off = self
|
||||||
|
|
@ -1587,7 +1591,9 @@ impl DexWriter {
|
||||||
(None, Some(method)) => (false, method),
|
(None, Some(method)) => (false, method),
|
||||||
_ => bail!(
|
_ => bail!(
|
||||||
"Unexpected configuration: method {} is both a direct and a virtual method in {}",
|
"Unexpected configuration: method {} is both a direct and a virtual method in {}",
|
||||||
method_id.__repr__(), class_id.__repr__()),
|
method_id.__repr__(),
|
||||||
|
class_id.__repr__()
|
||||||
|
),
|
||||||
};
|
};
|
||||||
if !method.parameters_annotations.is_empty() {
|
if !method.parameters_annotations.is_empty() {
|
||||||
let annotations_off = self
|
let annotations_off = self
|
||||||
|
|
@ -1763,12 +1769,12 @@ impl DexWriter {
|
||||||
debug!("Generate the map_list");
|
debug!("Generate the map_list");
|
||||||
// Get the size of a map item
|
// Get the size of a map item
|
||||||
let map_item_size = 12; /* = MapItem {
|
let map_item_size = 12; /* = MapItem {
|
||||||
type_: MapItemType::HeaderItem,
|
type_: MapItemType::HeaderItem,
|
||||||
unused: 0,
|
unused: 0,
|
||||||
size: 0,
|
size: 0,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
}
|
}
|
||||||
.size(); */
|
.size(); */
|
||||||
// Empty map has a size 4, then we add the size of a MapItem for each element
|
// Empty map has a size 4, then we add the size of a MapItem for each element
|
||||||
// The size of the map_list must be computed before generating the map list,
|
// The size of the map_list must be computed before generating the map list,
|
||||||
// as it affect the offset of some sections.
|
// as it affect the offset of some sections.
|
||||||
|
|
@ -2673,12 +2679,12 @@ impl SectionManager {
|
||||||
}
|
}
|
||||||
let mut map_list_size = 4;
|
let mut map_list_size = 4;
|
||||||
let map_item_size = 12; /* = MapItem {
|
let map_item_size = 12; /* = MapItem {
|
||||||
type_: MapItemType::HeaderItem,
|
type_: MapItemType::HeaderItem,
|
||||||
unused: 0,
|
unused: 0,
|
||||||
size: 0,
|
size: 0,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
}
|
}
|
||||||
.size(); */
|
.size(); */
|
||||||
for section in Section::VARIANT_LIST {
|
for section in Section::VARIANT_LIST {
|
||||||
if !section.is_data()
|
if !section.is_data()
|
||||||
&& (self.get_nb_elt(*section) != 0 || section == &Section::MapList)
|
&& (self.get_nb_elt(*section) != 0 || section == &Section::MapList)
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,9 @@ impl HiddenApiDomain {
|
||||||
value += "test-api";
|
value += "test-api";
|
||||||
}
|
}
|
||||||
if self.unknown_flags != 0 {
|
if self.unknown_flags != 0 {
|
||||||
warn!("This attribut has an unknown hiddenapi domain set, this will not be display for compatibility with apktool");
|
warn!(
|
||||||
|
"This attribut has an unknown hiddenapi domain set, this will not be display for compatibility with apktool"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use crate::{Apk, IdType, Result};
|
||||||
use androscalpel_serializer::{
|
use androscalpel_serializer::{
|
||||||
DexFileReader, HeaderItem, Serializable, Sleb128, Uleb128, Uleb128p1,
|
DexFileReader, HeaderItem, Serializable, Sleb128, Uleb128, Uleb128p1,
|
||||||
};
|
};
|
||||||
use apk_frauder::{end_of_central_directory::EndCentralDirectory, ZipFileReader};
|
use apk_frauder::{ZipFileReader, end_of_central_directory::EndCentralDirectory};
|
||||||
|
|
||||||
/// Convert an integer to the uleb128 byte encoding
|
/// Convert an integer to the uleb128 byte encoding
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
|
|
@ -111,13 +111,18 @@ pub fn replace_dex(
|
||||||
.map(PyBytesMethods::as_bytes)
|
.map(PyBytesMethods::as_bytes)
|
||||||
.map(Cursor::new)
|
.map(Cursor::new)
|
||||||
.collect();
|
.collect();
|
||||||
let additionnal_files: Option<HashMap<_, _>> = additionnal_files.as_ref().map(|additionnal_files|
|
let additionnal_files: Option<HashMap<_, _>> =
|
||||||
additionnal_files.iter()
|
additionnal_files.as_ref().map(|additionnal_files| {
|
||||||
.map(|(k, v)| (
|
additionnal_files
|
||||||
k.clone(),
|
.iter()
|
||||||
v.as_ref().map(|bytes| bytes.as_bytes()).map(Cursor::new)
|
.map(|(k, v)| {
|
||||||
)).collect()
|
(
|
||||||
);
|
k.clone(),
|
||||||
|
v.as_ref().map(|bytes| bytes.as_bytes()).map(Cursor::new),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
apk_frauder::replace_dex(
|
apk_frauder::replace_dex(
|
||||||
apk,
|
apk,
|
||||||
dst,
|
dst,
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,14 @@ use std::time::Instant;
|
||||||
fn write_to_report(data: &str) {
|
fn write_to_report(data: &str) {
|
||||||
static REPORT_FILE: Mutex<Option<File>> = Mutex::new(None);
|
static REPORT_FILE: Mutex<Option<File>> = Mutex::new(None);
|
||||||
let mut report_file = REPORT_FILE.lock().unwrap();
|
let mut report_file = REPORT_FILE.lock().unwrap();
|
||||||
let mut report_file = if let Some(report_file) = report_file.deref() {
|
let mut report_file = match report_file.deref() {
|
||||||
report_file
|
Some(report_file) => report_file,
|
||||||
} else {
|
_ => {
|
||||||
*report_file = Some(
|
*report_file = Some(
|
||||||
File::create(&format!("{}/test_repport.txt", env!("CARGO_MANIFEST_DIR"),)).unwrap(),
|
File::create(&format!("{}/test_repport.txt", env!("CARGO_MANIFEST_DIR"),)).unwrap(),
|
||||||
);
|
);
|
||||||
report_file.deref().as_ref().unwrap()
|
report_file.deref().as_ref().unwrap()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
writeln!(report_file, "{data}").unwrap();
|
writeln!(report_file, "{data}").unwrap();
|
||||||
}
|
}
|
||||||
|
|
@ -625,13 +626,16 @@ fn test_hidden_api() {
|
||||||
(
|
(
|
||||||
sj::Value::String(apktool_hiddenapi),
|
sj::Value::String(apktool_hiddenapi),
|
||||||
sj::Value::Null,
|
sj::Value::Null,
|
||||||
Some(HiddenApiData {
|
Some(HiddenApiData { permission, domain }),
|
||||||
permission,
|
) if (permission.to_smali_name() == apktool_hiddenapi)
|
||||||
domain,
|
&& &domain.to_smali_name() == "" =>
|
||||||
}),
|
{
|
||||||
) if (permission.to_smali_name() == apktool_hiddenapi) && &domain.to_smali_name() == "" => (),
|
()
|
||||||
|
}
|
||||||
|
|
||||||
_ => panic!("Expected {apktool_hiddenapi:?} and {apktool_hiddenapi_domain:?}, found {parsed_api:?} in {name}"),
|
_ => panic!(
|
||||||
|
"Expected {apktool_hiddenapi:?} and {apktool_hiddenapi_domain:?}, found {parsed_api:?} in {name}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let apktool_result = File::open(&apktool_result).expect("core-oj-33_hiddenapi.json not found");
|
let apktool_result = File::open(&apktool_result).expect("core-oj-33_hiddenapi.json not found");
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::io::{Read, Seek, SeekFrom};
|
||||||
|
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use androscalpel_serializer::{HeaderItem, Serializable};
|
use androscalpel_serializer::{HeaderItem, Serializable};
|
||||||
use apk_frauder::{end_of_central_directory::EndCentralDirectory, ZipFileReader};
|
use apk_frauder::{ZipFileReader, end_of_central_directory::EndCentralDirectory};
|
||||||
|
|
||||||
/// Test if a file is as .dex file an return the dex version if it is, else return None.
|
/// Test if a file is as .dex file an return the dex version if it is, else return None.
|
||||||
pub fn is_dex(file: &mut (impl Read + Seek)) -> Result<Option<usize>> {
|
pub fn is_dex(file: &mut (impl Read + Seek)) -> Result<Option<usize>> {
|
||||||
|
|
@ -18,16 +18,16 @@ pub fn is_dex(file: &mut (impl Read + Seek)) -> Result<Option<usize>> {
|
||||||
/// Test if a file is a zip file.
|
/// Test if a file is a zip file.
|
||||||
pub fn is_zip(mut file: impl Read + Seek) -> Result<bool> {
|
pub fn is_zip(mut file: impl Read + Seek) -> Result<bool> {
|
||||||
let pos = file.stream_position()?;
|
let pos = file.stream_position()?;
|
||||||
let ecd_off = if let Some(off) = ZipFileReader::get_end_of_central_directory_offset(&mut file) {
|
let ecd_off = match ZipFileReader::get_end_of_central_directory_offset(&mut file) {
|
||||||
off
|
Some(off) => off,
|
||||||
} else {
|
_ => {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
file.seek(SeekFrom::Start(ecd_off))?;
|
file.seek(SeekFrom::Start(ecd_off))?;
|
||||||
let r = if let Ok(sig) = apk_frauder::Signature::deserialize(&mut file) {
|
let r = match apk_frauder::Signature::deserialize(&mut file) {
|
||||||
EndCentralDirectory::SIGNATURE == sig
|
Ok(sig) => EndCentralDirectory::SIGNATURE == sig,
|
||||||
} else {
|
_ => false,
|
||||||
false
|
|
||||||
};
|
};
|
||||||
file.seek(SeekFrom::Start(pos))?;
|
file.seek(SeekFrom::Start(pos))?;
|
||||||
Ok(r)
|
Ok(r)
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ use std::collections::HashSet;
|
||||||
use pyo3::{exceptions::PyTypeError, prelude::*};
|
use pyo3::{exceptions::PyTypeError, prelude::*};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dex_id::*, scalar::*, DexAnnotation, DexString, MethodHandle, Result, Visitable, VisitableMut,
|
DexAnnotation, DexString, MethodHandle, Result, Visitable, VisitableMut, Visitor, VisitorMut,
|
||||||
Visitor, VisitorMut,
|
dex_id::*, scalar::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
//! The visitor trait and common implementations.
|
//! The visitor trait and common implementations.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ins::Instruction, scalar::*, Apk, CallSite, Class, Code, DexAnnotation, DexAnnotationItem,
|
Apk, CallSite, Class, Code, DexAnnotation, DexAnnotationItem, DexFile, DexString, DexValue,
|
||||||
DexFile, DexString, DexValue, Field, FieldVisibility, HiddenApiData, HiddenApiDomain,
|
Field, FieldVisibility, HiddenApiData, HiddenApiDomain, HiddenApiPermission, IdEnum, IdField,
|
||||||
HiddenApiPermission, IdEnum, IdField, IdMethod, IdMethodType, IdType, Method, MethodHandle,
|
IdMethod, IdMethodType, IdType, Method, MethodHandle, MethodVisibility, Result,
|
||||||
MethodVisibility, Result,
|
ins::Instruction, scalar::*,
|
||||||
};
|
};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -390,7 +390,9 @@ mod test {
|
||||||
fn serialize_u64() {
|
fn serialize_u64() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
0x123456789ABCDEF0u64.serialize_to_vec().unwrap(),
|
0x123456789ABCDEF0u64.serialize_to_vec().unwrap(),
|
||||||
vec![0xF0u8, 0xDEu8, 0xBCu8, 0x9Au8, 0x78u8, 0x56u8, 0x34u8, 0x12u8]
|
vec![
|
||||||
|
0xF0u8, 0xDEu8, 0xBCu8, 0x9Au8, 0x78u8, 0x56u8, 0x34u8, 0x12u8
|
||||||
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -357,26 +357,34 @@ mod test {
|
||||||
/// Test for bug found in <https://github.com/TkTech/mutf8/tree/master>:
|
/// Test for bug found in <https://github.com/TkTech/mutf8/tree/master>:
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tktech_bad_mutf8() {
|
fn test_tktech_bad_mutf8() {
|
||||||
assert!(TryInto::<String>::try_into(StringDataItem {
|
assert!(
|
||||||
utf16_size: Uleb128(0),
|
TryInto::<String>::try_into(StringDataItem {
|
||||||
data: vec![0x00]
|
utf16_size: Uleb128(0),
|
||||||
})
|
data: vec![0x00]
|
||||||
.is_err());
|
})
|
||||||
assert!(TryInto::<String>::try_into(StringDataItem {
|
.is_err()
|
||||||
utf16_size: Uleb128(0),
|
);
|
||||||
data: vec![0xC2]
|
assert!(
|
||||||
})
|
TryInto::<String>::try_into(StringDataItem {
|
||||||
.is_err());
|
utf16_size: Uleb128(0),
|
||||||
assert!(TryInto::<String>::try_into(StringDataItem {
|
data: vec![0xC2]
|
||||||
utf16_size: Uleb128(0),
|
})
|
||||||
data: vec![0xED]
|
.is_err()
|
||||||
})
|
);
|
||||||
.is_err());
|
assert!(
|
||||||
assert!(TryInto::<String>::try_into(StringDataItem {
|
TryInto::<String>::try_into(StringDataItem {
|
||||||
utf16_size: Uleb128(0),
|
utf16_size: Uleb128(0),
|
||||||
data: vec![0xE2]
|
data: vec![0xED]
|
||||||
})
|
})
|
||||||
.is_err());
|
.is_err()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
TryInto::<String>::try_into(StringDataItem {
|
||||||
|
utf16_size: Uleb128(0),
|
||||||
|
data: vec![0xE2]
|
||||||
|
})
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test from <https://github.com/TkTech/mutf8/tree/master>, test 2 bytes
|
/// Test from <https://github.com/TkTech/mutf8/tree/master>, test 2 bytes
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use crate as androscalpel_serializer;
|
use crate as androscalpel_serializer;
|
||||||
use crate::{
|
use crate::{
|
||||||
Error, ReadSeek, Result, Serializable, SerializableUntil, Sleb128, Uleb128, Uleb128p1, NO_INDEX,
|
Error, NO_INDEX, ReadSeek, Result, Serializable, SerializableUntil, Sleb128, Uleb128, Uleb128p1,
|
||||||
};
|
};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,7 @@ impl<'a> DexFileReader<'a> {
|
||||||
MapItemType::HeaderItem if item.offset != 0 || item.size != 1 => {
|
MapItemType::HeaderItem if item.offset != 0 || item.size != 1 => {
|
||||||
return Err(Error::InconsistantStruct(format!(
|
return Err(Error::InconsistantStruct(format!(
|
||||||
"Inconsistant Header Mapping info found in map_list: {item:x?}"
|
"Inconsistant Header Mapping info found in map_list: {item:x?}"
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
MapItemType::StringIdItem
|
MapItemType::StringIdItem
|
||||||
if item.offset != self.header.string_ids_off
|
if item.offset != self.header.string_ids_off
|
||||||
|
|
@ -280,7 +280,7 @@ impl<'a> DexFileReader<'a> {
|
||||||
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
||||||
header.string_ids_off: 0x{:x}, header.string_ids_size: {}",
|
header.string_ids_off: 0x{:x}, header.string_ids_size: {}",
|
||||||
self.header.string_ids_off, self.header.string_ids_size
|
self.header.string_ids_off, self.header.string_ids_size
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
MapItemType::TypeIdItem
|
MapItemType::TypeIdItem
|
||||||
if item.offset != self.header.type_ids_off
|
if item.offset != self.header.type_ids_off
|
||||||
|
|
@ -290,7 +290,7 @@ impl<'a> DexFileReader<'a> {
|
||||||
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
||||||
header.type_ids_off: 0x{:x}, header.type_ids_size: {}",
|
header.type_ids_off: 0x{:x}, header.type_ids_size: {}",
|
||||||
self.header.type_ids_off, self.header.type_ids_size
|
self.header.type_ids_off, self.header.type_ids_size
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
MapItemType::ProtoIdItem
|
MapItemType::ProtoIdItem
|
||||||
if item.offset != self.header.proto_ids_off
|
if item.offset != self.header.proto_ids_off
|
||||||
|
|
@ -300,7 +300,7 @@ impl<'a> DexFileReader<'a> {
|
||||||
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
||||||
header.proto_ids_off: 0x{:x}, header.proto_ids_size: {}",
|
header.proto_ids_off: 0x{:x}, header.proto_ids_size: {}",
|
||||||
self.header.proto_ids_off, self.header.proto_ids_size
|
self.header.proto_ids_off, self.header.proto_ids_size
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
MapItemType::FieldIdItem
|
MapItemType::FieldIdItem
|
||||||
if item.offset != self.header.field_ids_off
|
if item.offset != self.header.field_ids_off
|
||||||
|
|
@ -310,7 +310,7 @@ impl<'a> DexFileReader<'a> {
|
||||||
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
||||||
header.field_ids_off: 0x{:x}, header.field_ids_size: {}",
|
header.field_ids_off: 0x{:x}, header.field_ids_size: {}",
|
||||||
self.header.field_ids_off, self.header.field_ids_size
|
self.header.field_ids_off, self.header.field_ids_size
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
MapItemType::MethodIdItem
|
MapItemType::MethodIdItem
|
||||||
if item.offset != self.header.method_ids_off
|
if item.offset != self.header.method_ids_off
|
||||||
|
|
@ -320,7 +320,7 @@ impl<'a> DexFileReader<'a> {
|
||||||
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
||||||
header.method_ids_off: 0x{:x}, header.method_ids_size: {}",
|
header.method_ids_off: 0x{:x}, header.method_ids_size: {}",
|
||||||
self.header.method_ids_off, self.header.method_ids_size
|
self.header.method_ids_off, self.header.method_ids_size
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
MapItemType::ClassDefItem
|
MapItemType::ClassDefItem
|
||||||
if item.offset != self.header.class_defs_off
|
if item.offset != self.header.class_defs_off
|
||||||
|
|
@ -330,14 +330,14 @@ impl<'a> DexFileReader<'a> {
|
||||||
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
||||||
header.class_defs_off: 0x{:x}, header.class_defs_size: {}",
|
header.class_defs_off: 0x{:x}, header.class_defs_size: {}",
|
||||||
self.header.class_defs_off, self.header.class_defs_size
|
self.header.class_defs_off, self.header.class_defs_size
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
MapItemType::MapList if item.offset != self.header.map_off || item.size != 1 => {
|
MapItemType::MapList if item.offset != self.header.map_off || item.size != 1 => {
|
||||||
return Err(Error::InconsistantStruct(format!(
|
return Err(Error::InconsistantStruct(format!(
|
||||||
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
|
||||||
header.map_list_off: 0x{:x}",
|
header.map_list_off: 0x{:x}",
|
||||||
self.header.map_off
|
self.header.map_off
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
MapItemType::CallSiteIdItem => todo!(),
|
MapItemType::CallSiteIdItem => todo!(),
|
||||||
|
|
|
||||||
|
|
@ -977,7 +977,9 @@ mod test {
|
||||||
}
|
}
|
||||||
.serialize_to_vec()
|
.serialize_to_vec()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
vec![0x03, 0xb4, 0x01, 0x80, 0x02, 0xc7, 0x08, 0x8e, 0x02, 0xd9, 0x09, 0x87, 0x02,]
|
vec![
|
||||||
|
0x03, 0xb4, 0x01, 0x80, 0x02, 0xc7, 0x08, 0x8e, 0x02, 0xd9, 0x09, 0x87, 0x02,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
EncodedCatchHandler {
|
EncodedCatchHandler {
|
||||||
|
|
@ -999,7 +1001,9 @@ mod test {
|
||||||
}
|
}
|
||||||
.serialize_to_vec()
|
.serialize_to_vec()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
vec![0x03, 0xe9, 0x46, 0x56, 0xc7, 0x08, 0x8e, 0x02, 0xd9, 0x09, 0x87, 0x02,]
|
vec![
|
||||||
|
0x03, 0xe9, 0x46, 0x56, 0xc7, 0x08, 0x8e, 0x02, 0xd9, 0x09, 0x87, 0x02,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
EncodedCatchHandler {
|
EncodedCatchHandler {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue