finally fix this bug

This commit is contained in:
Jean-Marie Mineau 2025-04-23 17:36:20 +02:00
parent 09448752c0
commit 566b423c6b
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
8 changed files with 142 additions and 37 deletions

10
patcher/Cargo.lock generated
View file

@ -35,7 +35,7 @@ dependencies = [
[[package]]
name = "androscalpel"
version = "0.1.0"
source = "git+ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git?rev=1f2de8b#1f2de8b60daf3c2beba1193174e537175f939e4a"
source = "git+ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git?rev=110f0c0#110f0c0215337a27b3679c6a8ef447827fdcb658"
dependencies = [
"adler",
"androscalpel_platform_api_list",
@ -52,12 +52,12 @@ dependencies = [
[[package]]
name = "androscalpel_platform_api_list"
version = "0.1.0"
source = "git+ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git?rev=1f2de8b#1f2de8b60daf3c2beba1193174e537175f939e4a"
source = "git+ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git?rev=110f0c0#110f0c0215337a27b3679c6a8ef447827fdcb658"
[[package]]
name = "androscalpel_serializer"
version = "0.1.0"
source = "git+ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git?rev=1f2de8b#1f2de8b60daf3c2beba1193174e537175f939e4a"
source = "git+ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git?rev=110f0c0#110f0c0215337a27b3679c6a8ef447827fdcb658"
dependencies = [
"androscalpel_serializer_derive",
"log",
@ -66,7 +66,7 @@ dependencies = [
[[package]]
name = "androscalpel_serializer_derive"
version = "0.1.0"
source = "git+ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git?rev=1f2de8b#1f2de8b60daf3c2beba1193174e537175f939e4a"
source = "git+ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git?rev=110f0c0#110f0c0215337a27b3679c6a8ef447827fdcb658"
dependencies = [
"proc-macro2",
"quote",
@ -135,7 +135,7 @@ dependencies = [
[[package]]
name = "apk_frauder"
version = "0.1.0"
source = "git+ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git?rev=1f2de8b#1f2de8b60daf3c2beba1193174e537175f939e4a"
source = "git+ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git?rev=110f0c0#110f0c0215337a27b3679c6a8ef447827fdcb658"
dependencies = [
"androscalpel_serializer",
"anyhow",

View file

@ -6,9 +6,9 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
androscalpel = { git = "ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git", rev = "1f2de8b", features = ["code-analysis"] }
apk_frauder = { git = "ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git", rev = "1f2de8b"}
#androscalpel = { path = "../../androscalpel/androscalpel", features = ["code-analysis"] }
androscalpel = { git = "ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git", rev = "110f0c0", features = ["code-analysis", "platform-list"] }
apk_frauder = { git = "ssh://git@gitlab.inria.fr/androidoftheseus/androscalpel.git", rev = "110f0c0"}
#androscalpel = { path = "../../androscalpel/androscalpel", features = ["code-analysis", "platform-list"] }
#apk_frauder = { path = "../../androscalpel/apk_frauder"}
anyhow = { version = "1.0.95", features = ["backtrace"] }
clap = { version = "4.5.27", features = ["derive"] }

View file

@ -14,6 +14,7 @@ struct Cli {
method: String,
}
/// Usefull for debugging.
fn main() {
env_logger::init();
let cli = Cli::parse();

View file

@ -8,7 +8,7 @@ use androscalpel::{Apk, Class, IdType};
use androscalpel::SmaliName;
use patcher::{
code_loading_patcher::{insert_code, CodePatchingStrategy},
code_loading_patcher::{CodePatchingStrategy, insert_code},
labeling,
reflection_patcher::transform_method,
runtime_data::RuntimeData, // ReflectionInvokeData, ReflectionClassNewInstData, ReflectionCnstrNewInstData,

View file

@ -326,7 +326,7 @@ impl ClassLoader<'_> {
) -> Option<IdType> {
if ty.is_platform_class() {
// Platform classes have precedence for all android SDK classloader.
return Some(ty);
return Some(ty.clone());
}
if self.class == *DELEGATE_LAST_CLASS_LOADER {
if let Some(new_ty) = self.renamed_classes.get(ty) {
@ -341,7 +341,12 @@ impl ClassLoader<'_> {
return Some(new_ty);
}
} else {
log::warn!("Class Loader {}({}) has parent {}, but parent was not found in class loader list", self.id, self.class.__str__(), parent_id);
log::warn!(
"Class Loader {}({}) has parent {}, but parent was not found in class loader list",
self.id,
self.class.__str__(),
parent_id
);
}
}
if self.class == *DELEGATE_LAST_CLASS_LOADER {

View file

@ -1,5 +1,5 @@
use androscalpel::{IdMethod, IdType};
use anyhow::{bail, Result};
use anyhow::{Result, bail};
use std::sync::LazyLock;
pub(crate) static MTH_INVOKE: LazyLock<IdMethod> = LazyLock::new(|| {

View file

@ -1,7 +1,7 @@
use androscalpel::SmaliName;
use androscalpel::{Code, IdMethod, IdMethodType, IdType, Instruction, Method};
use anyhow::{bail, Context, Result};
use log::warn;
use log::{debug, warn};
use std::collections::HashMap;
use std::hash::{DefaultHasher, Hash, Hasher};
@ -35,27 +35,52 @@ pub fn transform_method(
// Get the available registers at the method level
let mut register_info = RegistersInfo::default();
debug!("Pathching method {}", meth.__str__());
// register_info.array_val is a wide reg, so need at least 0b1110 and 0b1111
if code.registers_size < 0b1111 {
register_info.array_val = code.registers_size as u8;
debug!(
"Use registers {}-{} for patching",
register_info.array_val,
register_info.array_val + 1
);
} else {
register_info.array_val = 0;
register_info.array_val_save = Some(code.registers_size);
debug!(
"Too many registers, reserve registers {}-{} to save registers later on",
code.registers_size,
code.registers_size + 1
);
}
if code.registers_size + 2 <= 0b1111 {
register_info.array_index = (code.registers_size + 2) as u8;
debug!("Use register {} for patching", register_info.array_index);
} else {
register_info.array_index = 0;
register_info.array_index_save = Some(code.registers_size + 2);
debug!(
"Too many registers, reserve register {} to save registers later on",
code.registers_size + 2
);
}
if code.registers_size + 3 <= 0b1111 {
register_info.array = (code.registers_size + 3) as u8;
debug!("Use register {} for patching", register_info.array);
} else {
register_info.array = 0;
register_info.array_save = Some(code.registers_size + 3);
debug!(
"Too many registers, reserve register {} to save registers later on",
code.registers_size + 3
);
}
register_info.first_arg = code.registers_size + 4;
register_info.nb_arg_reg = 0;
debug!(
"Will use register from {} on to store method arguments",
register_info.first_arg
);
register_info.nb_arg_reg = 0; // Will be set when saving args
let regs_type = if register_info.array_val_save.is_some()
|| register_info.array_index_save.is_some()
@ -76,19 +101,36 @@ pub fn transform_method(
|| method == &*CLASS_NEW_INST
|| method == &*CNSTR_NEW_INST)
&& current_addr_label.is_some() =>
{
'invoke_patch: {
let addr_label = current_addr_label.as_ref().unwrap();
let (pseudo_insns, move_ret) = get_move_result(iter.clone());
if move_ret.is_some() {
while move_ret.as_ref() != iter.next() {}
}
debug!(
"Patching reflection call of {} at {}:{}",
method.__str__(),
meth.__str__(),
addr_label
);
let end_label = if method == &*MTH_INVOKE {
format!("end_reflection_call_at_{}", addr_label.clone())
} else if method == &*CLASS_NEW_INST || method == &*CNSTR_NEW_INST {
format!("end_reflection_instanciation_at_{}", addr_label.clone())
} else {
panic!("Should not happen!")
// This should not happen, cf the guard on the match
warn!(
"Reflection Data point to an invoke-virtual {}, (expected invocation of {}, {} or {})",
method.__str__(),
MTH_INVOKE.__str__(),
CLASS_NEW_INST.__str__(),
CNSTR_NEW_INST.__str__()
);
new_insns.push(ins.clone());
break 'invoke_patch;
};
let (pseudo_insns, move_ret) = get_move_result(iter.clone());
if move_ret.is_some() {
while move_ret.as_ref() != iter.next() {}
}
let mut restore_reg = vec![];
if let Some(regs_type) = regs_type.as_ref() {
if (method == &*MTH_INVOKE && invoke_data.contains_key(addr_label))
@ -125,7 +167,7 @@ pub fn transform_method(
new_insns.push(move_ret.clone());
}
current_addr_label = None;
continue;
break 'invoke_patch;
}
}
}
@ -133,6 +175,12 @@ pub fn transform_method(
// TODO: recover from failure
if method == &*MTH_INVOKE {
for ref_data in invoke_data.get(addr_label).unwrap_or(&vec![]) {
debug!(
"Patching reflection call at {}:{} to {}",
meth.__str__(),
addr_label,
ref_data.method.__str__()
);
for ins in get_invoke_block(
ref_data,
args.as_slice(),
@ -147,6 +195,12 @@ pub fn transform_method(
}
} else if method == &*CLASS_NEW_INST {
for ref_data in class_new_inst_data.get(addr_label).unwrap_or(&vec![]) {
debug!(
"Patching reflection instantion at {}:{} for {}",
meth.__str__(),
addr_label,
ref_data.constructor.__str__()
);
for ins in get_class_new_inst_block(
ref_data,
args.as_slice(),
@ -159,6 +213,12 @@ pub fn transform_method(
}
} else if method == &*CNSTR_NEW_INST {
for ref_data in cnstr_new_inst_data.get(addr_label).unwrap_or(&vec![]) {
debug!(
"Patching reflection instantion at {}:{} for {}",
meth.__str__(),
addr_label,
ref_data.constructor.__str__()
);
for ins in get_cnstr_new_inst_block(
ref_data,
args.as_slice(),
@ -209,30 +269,53 @@ pub fn transform_method(
let mut i = 0;
if !meth.is_static {
// Non static method take 'this' as first argument
let new_arg_reg = code.registers_size - ins_size + i + register_info.get_nb_added_reg();
let old_arg_reg = code.registers_size - ins_size + i;
debug!(
"Move `this` argument from new argument register {} to original register {}",
new_arg_reg, old_arg_reg
);
code.insns.push(Instruction::MoveObject {
from: code.registers_size - ins_size + i + register_info.get_nb_added_reg(),
to: code.registers_size - ins_size + i,
from: new_arg_reg,
to: old_arg_reg,
});
i += 1;
}
for arg in &meth.descriptor.proto.get_parameters() {
let new_arg_reg = code.registers_size - ins_size + i + register_info.get_nb_added_reg();
let old_arg_reg = code.registers_size - ins_size + i;
if arg.is_class() || arg.is_array() {
code.insns.push(Instruction::MoveObject {
from: code.registers_size - ins_size + i + register_info.get_nb_added_reg(),
to: code.registers_size - ins_size + i,
from: new_arg_reg,
to: old_arg_reg,
});
debug!(
"Move reference argument from new argument register {} to original register {}",
new_arg_reg, old_arg_reg
);
i += 1;
} else if arg.is_long() || arg.is_double() {
code.insns.push(Instruction::MoveWide {
from: code.registers_size - ins_size + i + register_info.get_nb_added_reg(),
to: code.registers_size - ins_size + i,
from: new_arg_reg,
to: old_arg_reg,
});
debug!(
"Move wide argument from new argument registers {}-{} to original registers {}-{}",
new_arg_reg,
new_arg_reg + 1,
old_arg_reg,
new_arg_reg + 1
);
i += 2;
} else {
code.insns.push(Instruction::Move {
from: code.registers_size - ins_size + i + register_info.get_nb_added_reg(),
to: code.registers_size - ins_size + i,
from: new_arg_reg,
to: old_arg_reg,
});
debug!(
"Move scalar argument from new argument register {} to original register {}",
new_arg_reg, old_arg_reg
);
i += 1;
}
}

View file

@ -1,5 +1,6 @@
use androscalpel::{Instruction, RegType};
use anyhow::{bail, Result};
use log::debug;
/// Information about the register used.
///
@ -125,10 +126,10 @@ impl RegistersInfo {
if regs_type[i + 1] == RegType::Object {
save_reg_insns.push(Instruction::MoveObject {
from: (i + 1) as u16,
to: reg_save,
to: reg_save + 1,
});
restore_reg_insns.push(Instruction::MoveObject {
from: reg_save,
from: reg_save + 1,
to: (i + 1) as u16,
});
} else if regs_type[i + 1] == RegType::SimpleScalar
@ -137,10 +138,10 @@ impl RegistersInfo {
{
save_reg_insns.push(Instruction::Move {
from: (i + 1) as u16,
to: reg_save,
to: reg_save + 1,
});
restore_reg_insns.push(Instruction::Move {
from: reg_save,
from: reg_save + 1,
to: (i + 1) as u16,
});
} // else RegType::Undefined, do nothing, just use it
@ -199,10 +200,10 @@ impl RegistersInfo {
if regs_type[i + 1] == RegType::Object {
save_reg_insns.push(Instruction::MoveObject {
from: (i + 1) as u16,
to: reg_save,
to: reg_save + 1,
});
restore_reg_insns.push(Instruction::MoveObject {
from: reg_save,
from: reg_save + 1,
to: (i + 1) as u16,
});
} else if regs_type[i + 1] == RegType::SimpleScalar
@ -211,10 +212,10 @@ impl RegistersInfo {
{
save_reg_insns.push(Instruction::Move {
from: (i + 1) as u16,
to: reg_save,
to: reg_save + 1,
});
restore_reg_insns.push(Instruction::Move {
from: reg_save,
from: reg_save + 1,
to: (i + 1) as u16,
});
} // else RegType::Undefined, do nothing, just use it
@ -226,6 +227,13 @@ impl RegistersInfo {
bail!("Could not found enough usable registers to patch the method")
}
}
debug!(
"Temporarily reserve registers {}-{} and save their values to {}-{}",
self.array_val,
self.array_val + 1,
reg_save,
reg_save + 1
);
}
if let Some(reg_save) = self.array_index_save {
let mut found = false;
@ -318,6 +326,10 @@ impl RegistersInfo {
bail!("Could not found enough usable registers to patch the method")
}
}
debug!(
"Temporarily reserve register {} and save it value to {}",
self.array_index, reg_save,
);
}
if let Some(reg_save) = self.array_save {
let mut found = false;
@ -402,6 +414,10 @@ impl RegistersInfo {
bail!("Could not found enough usable registers to patch the method")
}
}
debug!(
"Temporarily reserve register {} and save it value to {}",
self.array, reg_save,
);
}
Ok((save_reg_insns, restore_reg_insns))
}