update rust edition of androscalpel

This commit is contained in:
Jean-Marie Mineau 2025-04-18 11:12:11 +02:00
parent d4ccc73362
commit 218d6bf6fc
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
20 changed files with 223 additions and 167 deletions

View file

@ -1,7 +1,7 @@
[package]
name = "androscalpel"
version = "0.1.0"
edition = "2021"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]

View file

@ -7,8 +7,8 @@ use pyo3::prelude::*;
use crate::hashmap_vectorize;
use crate::{
dex_id::IdType, value::DexValue, DexString, IdField, IdMethod, IdMethodType, MethodHandle,
Result, Visitable, VisitableMut, Visitor, VisitorMut,
DexString, IdField, IdMethod, IdMethodType, MethodHandle, Result, Visitable, VisitableMut,
Visitor, VisitorMut, dex_id::IdType, value::DexValue,
};
/// Annotation with a visibility

View file

@ -1,6 +1,6 @@
//! Representation of an apk.
use anyhow::{anyhow, bail, Context};
use anyhow::{Context, anyhow, bail};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::fs::File;
@ -11,9 +11,9 @@ use log::{error, info};
#[cfg(feature = "python")]
use pyo3::{prelude::*, types::PyBytes};
use crate::Result;
use crate::ins::CallSite;
use crate::instructions;
use crate::Result;
use crate::*;
use androscalpel_serializer::Instruction as InsFormat;
use androscalpel_serializer::*;
@ -1030,7 +1030,9 @@ impl Apk {
Format11X { op: 0x27, va } => Instruction::Throw { reg: va },
Format10T { op: 0x28, a } => {
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 {
addr + a as usize
@ -1043,7 +1045,9 @@ impl Apk {
}
Format20T { op: 0x29, a } => {
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 {
addr + a as usize
@ -1056,7 +1060,9 @@ impl Apk {
}
Format30T { op: 0x2a, a } => {
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 {
addr + a as usize
@ -3053,11 +3059,14 @@ impl Apk {
i += 1;
dex_name = format!("classes{i}.dex");
}
if let Some(_version) = crate::utils::is_dex(&mut code)? {
match crate::utils::is_dex(&mut code)? {
Some(_version) => {
let mut data = vec![];
std::io::copy(&mut code, &mut data)?;
self.add_dex_file(&dex_name, &data, label_ins, cache)
} else if crate::utils::is_zip(&mut code)? {
}
_ => {
if crate::utils::is_zip(&mut code)? {
let mut tmp_apk = Apk::load_apk(code, label_ins, cache)?;
let mut j = 1;
let mut tmp_dex_name: String = "classes.dex".into();
@ -3073,6 +3082,8 @@ impl Apk {
bail!("Could not recognize the type of the input file")
}
}
}
}
/// Add the content of a dex file to the apk.
///

View file

@ -9,8 +9,8 @@ use std::collections::{HashMap, HashSet};
use pyo3::prelude::*;
use crate::{
ins::Instruction, DexString, IdField, IdMethod, IdMethodType, IdType, Method, MethodHandle,
Result, Visitable, VisitableMut, Visitor, VisitorMut,
DexString, IdField, IdMethod, IdMethodType, IdType, Method, MethodHandle, Result, Visitable,
VisitableMut, Visitor, VisitorMut, ins::Instruction,
};
// TODO: make this easy to edit/manipulate, maybe move to Method

View file

@ -293,19 +293,24 @@ impl<'a> MethodCFG<'a> {
" node_{i} [shape=record,style=filled,fillcolor=lightgrey,label=\"{label}\"];\n\n"
);
}
dot_string +=
" node_end [shape=record,style=filled,fillcolor=lightgrey,label=\"{\\< EXIT \\>}\"];\n\n";
dot_string += " node_end [shape=record,style=filled,fillcolor=lightgrey,label=\"{\\< EXIT \\>}\"];\n\n";
for (i, node) in self.nodes.iter().enumerate() {
for j in &node.next_nodes {
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 {
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() {
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";

View file

@ -123,7 +123,9 @@ impl MethodCFG<'_> {
node_label += reg.to_str();
}
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";

View file

@ -5,11 +5,11 @@ use std::cmp::{Ord, Ordering, PartialOrd};
use std::collections::HashSet;
use std::hash::Hash;
use anyhow::{anyhow, bail, Context};
use anyhow::{Context, anyhow, bail};
#[cfg(feature = "python")]
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};
/// The type of a method. The shorty is formated as described in
@ -332,15 +332,18 @@ impl IdType {
'J' => Some(Self::long()),
'F' => Some(Self::float()),
'D' => Some(Self::double()),
'[' => { array_dimmention += 1; None },
'[' => {
array_dimmention += 1;
None
}
'L' => {
let mut class_name = String::new();
for cc in chars.by_ref(){
if cc == ';' { break;}
else {
for cc in chars.by_ref() {
if cc == ';' {
break;
} else {
class_name.push(cc);
}
}
Some(Self::class(&class_name))
}
@ -350,13 +353,13 @@ impl IdType {
};
if let Some(mut new_type) = new_type {
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] {
*c = 0x5b;
}
data[array_dimmention..].copy_from_slice(&new_type.0 .0.data);
new_type.0 .0.data = data;
new_type.0 .0.utf16_size.0 += array_dimmention as u32;
data[array_dimmention..].copy_from_slice(&new_type.0.0.data);
new_type.0.0.data = data;
new_type.0.0.utf16_size.0 += array_dimmention as u32;
}
lst.push(new_type);
@ -443,8 +446,8 @@ impl IdType {
#[cfg_attr(feature = "python", staticmethod)]
pub fn array(type_: &IdType) -> Self {
let mut ty = type_.clone();
ty.0 .0.utf16_size.0 += 1;
ty.0 .0.data.insert(0, 0x5b);
ty.0.0.utf16_size.0 += 1;
ty.0.0.data.insert(0, 0x5b);
ty
}
@ -644,7 +647,7 @@ impl IdType {
impl SmaliName for IdType {
/// Convert a descriptor to its smali representation.
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)
}
/// Convert a smali representation to its descriptor.

View file

@ -27,7 +27,8 @@ impl<V: VisitorMut> VisitableMut<V> for DexString {
impl std::fmt::Debug for DexString {
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) {
Ok(string) => {
f.write_str(&format!(
"DexString({}, {:#x})",
string, self.0.utf16_size.0
@ -38,7 +39,8 @@ impl std::fmt::Debug for DexString {
.field(&self.0.utf16_size.0)
.finish()
*/
} else {
}
_ => {
f.write_str(&format!(
"DexString({:?}, {:#x})",
self.0.data, self.0.utf16_size.0
@ -50,6 +52,7 @@ impl std::fmt::Debug for DexString {
*/
}
}
}
}
impl Serialize for DexString {
@ -193,12 +196,13 @@ impl DexString {
}
pub fn __str__(&self) -> String {
if let Ok(string) = TryInto::<String>::try_into(self) {
string
} else {
match TryInto::<String>::try_into(self) {
Ok(string) => string,
_ => {
format!("string{:02x?}", self.0.data)
}
}
}
pub fn __repr__(&self) -> String {
format!("{:?}", self)

View file

@ -5,7 +5,7 @@ use std::io;
use std::io::{Cursor, Seek, SeekFrom, Write};
use adler::Adler32;
use anyhow::{anyhow, bail, Context};
use anyhow::{Context, anyhow, bail};
use log::{debug, warn};
use sha1::{Digest, Sha1};
@ -707,7 +707,7 @@ impl DexWriter {
.handlers
.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!(
"Label {} not found in code of {}, but found try \
with this label as catch all",
@ -1524,7 +1524,9 @@ impl DexWriter {
(None, Some(field)) => (false, field),
_ => bail!(
"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() {
let annotations_off = self
@ -1558,7 +1560,9 @@ impl DexWriter {
(None, Some(method)) => (false, method),
_ => bail!(
"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() {
let annotations_off = self
@ -1587,7 +1591,9 @@ impl DexWriter {
(None, Some(method)) => (false, method),
_ => bail!(
"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() {
let annotations_off = self

View file

@ -170,7 +170,9 @@ impl HiddenApiDomain {
value += "test-api";
}
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
}

View file

@ -12,7 +12,7 @@ use crate::{Apk, IdType, Result};
use androscalpel_serializer::{
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
#[pyfunction]
@ -111,13 +111,18 @@ pub fn replace_dex(
.map(PyBytesMethods::as_bytes)
.map(Cursor::new)
.collect();
let additionnal_files: Option<HashMap<_, _>> = additionnal_files.as_ref().map(|additionnal_files|
additionnal_files.iter()
.map(|(k, v)| (
let additionnal_files: Option<HashMap<_, _>> =
additionnal_files.as_ref().map(|additionnal_files| {
additionnal_files
.iter()
.map(|(k, v)| {
(
k.clone(),
v.as_ref().map(|bytes| bytes.as_bytes()).map(Cursor::new)
)).collect()
);
v.as_ref().map(|bytes| bytes.as_bytes()).map(Cursor::new),
)
})
.collect()
});
apk_frauder::replace_dex(
apk,
dst,

View file

@ -13,13 +13,14 @@ use std::time::Instant;
fn write_to_report(data: &str) {
static REPORT_FILE: Mutex<Option<File>> = Mutex::new(None);
let mut report_file = REPORT_FILE.lock().unwrap();
let mut report_file = if let Some(report_file) = report_file.deref() {
report_file
} else {
let mut report_file = match report_file.deref() {
Some(report_file) => report_file,
_ => {
*report_file = Some(
File::create(&format!("{}/test_repport.txt", env!("CARGO_MANIFEST_DIR"),)).unwrap(),
);
report_file.deref().as_ref().unwrap()
}
};
writeln!(report_file, "{data}").unwrap();
}
@ -625,13 +626,16 @@ fn test_hidden_api() {
(
sj::Value::String(apktool_hiddenapi),
sj::Value::Null,
Some(HiddenApiData {
permission,
domain,
}),
) if (permission.to_smali_name() == apktool_hiddenapi) && &domain.to_smali_name() == "" => (),
Some(HiddenApiData { permission, domain }),
) 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");

View file

@ -2,7 +2,7 @@ use std::io::{Read, Seek, SeekFrom};
use crate::Result;
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.
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.
pub fn is_zip(mut file: impl Read + Seek) -> Result<bool> {
let pos = file.stream_position()?;
let ecd_off = if let Some(off) = ZipFileReader::get_end_of_central_directory_offset(&mut file) {
off
} else {
let ecd_off = match ZipFileReader::get_end_of_central_directory_offset(&mut file) {
Some(off) => off,
_ => {
return Ok(false);
}
};
file.seek(SeekFrom::Start(ecd_off))?;
let r = if let Ok(sig) = apk_frauder::Signature::deserialize(&mut file) {
EndCentralDirectory::SIGNATURE == sig
} else {
false
let r = match apk_frauder::Signature::deserialize(&mut file) {
Ok(sig) => EndCentralDirectory::SIGNATURE == sig,
_ => false,
};
file.seek(SeekFrom::Start(pos))?;
Ok(r)

View file

@ -7,8 +7,8 @@ use std::collections::HashSet;
use pyo3::{exceptions::PyTypeError, prelude::*};
use crate::{
dex_id::*, scalar::*, DexAnnotation, DexString, MethodHandle, Result, Visitable, VisitableMut,
Visitor, VisitorMut,
DexAnnotation, DexString, MethodHandle, Result, Visitable, VisitableMut, Visitor, VisitorMut,
dex_id::*, scalar::*,
};
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]

View file

@ -1,10 +1,10 @@
//! The visitor trait and common implementations.
use crate::{
ins::Instruction, scalar::*, Apk, CallSite, Class, Code, DexAnnotation, DexAnnotationItem,
DexFile, DexString, DexValue, Field, FieldVisibility, HiddenApiData, HiddenApiDomain,
HiddenApiPermission, IdEnum, IdField, IdMethod, IdMethodType, IdType, Method, MethodHandle,
MethodVisibility, Result,
Apk, CallSite, Class, Code, DexAnnotation, DexAnnotationItem, DexFile, DexString, DexValue,
Field, FieldVisibility, HiddenApiData, HiddenApiDomain, HiddenApiPermission, IdEnum, IdField,
IdMethod, IdMethodType, IdType, Method, MethodHandle, MethodVisibility, Result,
ins::Instruction, scalar::*,
};
use std::collections::HashSet;

View file

@ -390,7 +390,9 @@ mod test {
fn serialize_u64() {
assert_eq!(
0x123456789ABCDEF0u64.serialize_to_vec().unwrap(),
vec![0xF0u8, 0xDEu8, 0xBCu8, 0x9Au8, 0x78u8, 0x56u8, 0x34u8, 0x12u8]
vec![
0xF0u8, 0xDEu8, 0xBCu8, 0x9Au8, 0x78u8, 0x56u8, 0x34u8, 0x12u8
]
);
}

View file

@ -357,26 +357,34 @@ mod test {
/// Test for bug found in <https://github.com/TkTech/mutf8/tree/master>:
#[test]
fn test_tktech_bad_mutf8() {
assert!(TryInto::<String>::try_into(StringDataItem {
assert!(
TryInto::<String>::try_into(StringDataItem {
utf16_size: Uleb128(0),
data: vec![0x00]
})
.is_err());
assert!(TryInto::<String>::try_into(StringDataItem {
.is_err()
);
assert!(
TryInto::<String>::try_into(StringDataItem {
utf16_size: Uleb128(0),
data: vec![0xC2]
})
.is_err());
assert!(TryInto::<String>::try_into(StringDataItem {
.is_err()
);
assert!(
TryInto::<String>::try_into(StringDataItem {
utf16_size: Uleb128(0),
data: vec![0xED]
})
.is_err());
assert!(TryInto::<String>::try_into(StringDataItem {
.is_err()
);
assert!(
TryInto::<String>::try_into(StringDataItem {
utf16_size: Uleb128(0),
data: vec![0xE2]
})
.is_err());
.is_err()
);
}
/// Test from <https://github.com/TkTech/mutf8/tree/master>, test 2 bytes

View file

@ -2,7 +2,7 @@
use crate as androscalpel_serializer;
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;

View file

@ -270,7 +270,7 @@ impl<'a> DexFileReader<'a> {
MapItemType::HeaderItem if item.offset != 0 || item.size != 1 => {
return Err(Error::InconsistantStruct(format!(
"Inconsistant Header Mapping info found in map_list: {item:x?}"
)))
)));
}
MapItemType::StringIdItem
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?}, \
header.string_ids_off: 0x{:x}, header.string_ids_size: {}",
self.header.string_ids_off, self.header.string_ids_size
)))
)));
}
MapItemType::TypeIdItem
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?}, \
header.type_ids_off: 0x{:x}, header.type_ids_size: {}",
self.header.type_ids_off, self.header.type_ids_size
)))
)));
}
MapItemType::ProtoIdItem
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?}, \
header.proto_ids_off: 0x{:x}, header.proto_ids_size: {}",
self.header.proto_ids_off, self.header.proto_ids_size
)))
)));
}
MapItemType::FieldIdItem
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?}, \
header.field_ids_off: 0x{:x}, header.field_ids_size: {}",
self.header.field_ids_off, self.header.field_ids_size
)))
)));
}
MapItemType::MethodIdItem
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?}, \
header.method_ids_off: 0x{:x}, header.method_ids_size: {}",
self.header.method_ids_off, self.header.method_ids_size
)))
)));
}
MapItemType::ClassDefItem
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?}, \
header.class_defs_off: 0x{:x}, 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 => {
return Err(Error::InconsistantStruct(format!(
"Inconsistant MapList Mapping info found in map_list: {item:x?}, \
header.map_list_off: 0x{:x}",
self.header.map_off
)))
)));
}
/*
MapItemType::CallSiteIdItem => todo!(),

View file

@ -977,7 +977,9 @@ mod test {
}
.serialize_to_vec()
.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!(
EncodedCatchHandler {
@ -999,7 +1001,9 @@ mod test {
}
.serialize_to_vec()
.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!(
EncodedCatchHandler {