patcher side of things

This commit is contained in:
Jean-Marie Mineau 2025-03-27 16:12:00 +01:00
parent 0a77cf2efb
commit f8c70b576b
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
2 changed files with 128 additions and 28 deletions

View file

@ -17,7 +17,7 @@ pub enum CodePatchingStrategy {
pub fn insert_code( pub fn insert_code(
strategy: CodePatchingStrategy, strategy: CodePatchingStrategy,
apk: &mut Apk, apk: &mut Apk,
data: &RuntimeData, data: &mut RuntimeData,
) -> Result<()> { ) -> Result<()> {
match strategy { match strategy {
CodePatchingStrategy::Naive => insert_code_naive(apk, data), CodePatchingStrategy::Naive => insert_code_naive(apk, data),
@ -37,11 +37,11 @@ fn insert_code_naive(apk: &mut Apk, data: &RuntimeData) -> Result<()> {
Ok(()) Ok(())
} }
fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result<()> { fn insert_code_model_class_loaders(apk: &mut Apk, runtime_data: &mut RuntimeData) -> Result<()> {
let mut class_defined = apk.list_classes(); let mut class_defined = apk.list_classes();
let mut class_redefined = HashSet::new(); let mut class_redefined = HashSet::new();
let mut class_loaders = HashMap::new(); let mut class_loaders = HashMap::new();
let main_cl_id = "MAIN".to_string(); let main_cl_id = runtime_data.apk_cl_id.clone();
class_loaders.insert( class_loaders.insert(
main_cl_id.clone(), main_cl_id.clone(),
ClassLoader { ClassLoader {
@ -53,7 +53,7 @@ fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result<
}, },
); );
// -- Rename class def -- // -- Rename class def --
for dyn_data in &data.dyn_code_load { for dyn_data in &runtime_data.dyn_code_load {
let mut apk = Apk::new(); let mut apk = Apk::new();
let class = dyn_data.classloader_class.clone(); let class = dyn_data.classloader_class.clone();
for file in &dyn_data.files { for file in &dyn_data.files {
@ -82,12 +82,14 @@ fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result<
} }
// -- Rename class ref -- // -- Rename class ref --
let mut new_names: HashMap<_, _> = class_loaders let mut renamers: HashMap<_, _> = class_loaders
.values() .values()
.map(|cl| { .map(|cl| {
( (
cl.id.clone(), cl.id.clone(),
cl.get_ref_new_names(&class_redefined, &class_loaders), RenameTypeVisitor {
new_names: cl.get_ref_new_names(&class_redefined, &class_loaders),
},
) )
}) })
.collect(); .collect();
@ -98,9 +100,9 @@ fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result<
// and anyway, there is no reason to rename ref in it (its only parent // and anyway, there is no reason to rename ref in it (its only parent
// sould be the classbootloader, and we don't handle changing class loader // sould be the classbootloader, and we don't handle changing class loader
// on the fly) // on the fly)
if let Some(new_names) = new_names.remove(&k) { if let Some(renamer) = renamers.get_mut(&k) {
if k != main_cl_id { if k != main_cl_id {
v.rename_refs(new_names).map(|v| (k, v)) v.rename_refs(renamer).map(|v| (k, v))
} else { } else {
Ok((k, v)) Ok((k, v))
} }
@ -110,24 +112,97 @@ fn insert_code_model_class_loaders(apk: &mut Apk, data: &RuntimeData) -> Result<
}) })
.collect::<Result<_>>()?; .collect::<Result<_>>()?;
// TODO: get the ClassLoader::parent values... // -- update Runtime Data to reflect the name change --
// TODO: model the delegation behavior and rename ref to class accordingly runtime_data.invoke_data.iter_mut().for_each(|data| {
if let Some(visitor) = renamers.get_mut(&data.method_cl_id) {
match visitor.visit_method_id(data.method.clone()) {
Err(err) => log::warn!(
"Failed to generate new name for {} from {}: {err}",
data.method.__str__(),
data.method_cl_id
),
Ok(new_method) => data.renamed_method = Some(new_method),
}
}
if let Some(visitor) = renamers.get_mut(&data.caller_cl_id) {
match visitor.visit_method_id(data.caller_method.clone()) {
Err(err) => log::warn!(
"Failed to generate new name for {} from {}: {err}",
data.caller_method.__str__(),
data.caller_cl_id
),
Ok(new_method) => data.renamed_caller_method = Some(new_method),
}
}
});
runtime_data
.class_new_inst_data
.iter_mut()
.for_each(|data| {
if let Some(visitor) = renamers.get_mut(&data.constructor_cl_id) {
match visitor.visit_method_id(data.constructor.clone()) {
Err(err) => log::warn!(
"Failed to generate new name for {} from {}: {err}",
data.constructor.__str__(),
data.constructor_cl_id
),
Ok(new_method) => data.renamed_constructor = Some(new_method),
}
}
if let Some(visitor) = renamers.get_mut(&data.caller_cl_id) {
match visitor.visit_method_id(data.caller_method.clone()) {
Err(err) => log::warn!(
"Failed to generate new name for {} from {}: {err}",
data.caller_method.__str__(),
data.caller_cl_id
),
Ok(new_method) => data.renamed_caller_method = Some(new_method),
}
}
});
runtime_data
.cnstr_new_inst_data
.iter_mut()
.for_each(|data| {
if let Some(visitor) = renamers.get_mut(&data.constructor_cl_id) {
match visitor.visit_method_id(data.constructor.clone()) {
Err(err) => log::warn!(
"Failed to generate new name for {} from {}: {err}",
data.constructor.__str__(),
data.constructor_cl_id
),
Ok(new_method) => data.renamed_constructor = Some(new_method),
}
}
if let Some(visitor) = renamers.get_mut(&data.caller_cl_id) {
match visitor.visit_method_id(data.caller_method.clone()) {
Err(err) => log::warn!(
"Failed to generate new name for {} from {}: {err}",
data.caller_method.__str__(),
data.caller_cl_id
),
Ok(new_method) => data.renamed_caller_method = Some(new_method),
}
}
});
// TODO: update Runtime Data to reflect the name change // -- inject code to apk --
let apk = match class_loaders.remove(&main_cl_id).unwrap().apk {
let apk = match class_loaders.remove(&main_cl_id).unwrap().apk { ApkOrRef::Ref(apk) => { ApkOrRef::Ref(apk) => apk,
apk _ => {
} _ => { panic!("Main APK is not stored as ref?")
panic!("Main APK is not stored as ref?") }
}}; };
for (_, ClassLoader { apk: other, .. }) in class_loaders.into_iter() { for (_, ClassLoader { apk: other, .. }) in class_loaders.into_iter() {
match other { ApkOrRef::Owned(other) => { match other {
apk.merge(other); ApkOrRef::Owned(other) => {
} _ => { apk.merge(other);
panic!("Secondary APK is not stored as owned?") }
}} _ => {
panic!("Secondary APK is not stored as owned?")
}
}
} }
//todo!()
Ok(()) Ok(())
} }
@ -234,11 +309,10 @@ impl ClassLoader<'_> {
} }
} }
pub fn rename_refs(self, new_names: HashMap<IdType, IdType>) -> Result<Self> { pub fn rename_refs(self, renamer: &mut RenameTypeVisitor) -> Result<Self> {
let mut visitor = RenameTypeVisitor { new_names };
Ok(Self { Ok(Self {
apk: match self.apk { apk: match self.apk {
ApkOrRef::Owned(apk) => match visitor.visit_apk(apk) { ApkOrRef::Owned(apk) => match renamer.visit_apk(apk) {
Err(err) => { Err(err) => {
log::error!( log::error!(
"Failed to rename refs in apk of {}({})): {err}", "Failed to rename refs in apk of {}({})): {err}",

View file

@ -10,6 +10,8 @@ pub struct RuntimeData {
pub class_new_inst_data: Vec<ReflectionClassNewInstData>, pub class_new_inst_data: Vec<ReflectionClassNewInstData>,
pub cnstr_new_inst_data: Vec<ReflectionCnstrNewInstData>, pub cnstr_new_inst_data: Vec<ReflectionCnstrNewInstData>,
pub dyn_code_load: Vec<DynamicCodeLoadingData>, pub dyn_code_load: Vec<DynamicCodeLoadingData>,
/// The id of the class loader of the apk (the main classloader)
pub apk_cl_id: String,
} }
impl RuntimeData { impl RuntimeData {
@ -91,10 +93,18 @@ impl RuntimeData {
/// `java.lang.reflect.Method.invoke()`. /// `java.lang.reflect.Method.invoke()`.
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)] #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct ReflectionInvokeData { pub struct ReflectionInvokeData {
/// The method called by `java.lang.reflect.Method.invoke()` /// The method called by `java.lang.reflect.Method.invoke()` (at runtime)
pub method: IdMethod, pub method: IdMethod,
/// The method calling `java.lang.reflect.Method.invoke()` /// The id of the classloader defining the constructor
pub method_cl_id: String,
/// The name of the method to call statically.
pub renamed_method: Option<IdMethod>,
/// The method calling `java.lang.reflect.Method.invoke()` (at runtime)
pub caller_method: IdMethod, pub caller_method: IdMethod,
/// The id of the classloader defining the caller method
pub caller_cl_id: String,
/// The name of the method that call the method (statically)
pub renamed_caller_method: Option<IdMethod>,
/// Address where the call to `java.lang.reflect.Method.invoke()` was made in `caller_method`. /// Address where the call to `java.lang.reflect.Method.invoke()` was made in `caller_method`.
pub addr: usize, pub addr: usize,
/// If the method is static (static method don't take 'this' as argument) /// If the method is static (static method don't take 'this' as argument)
@ -108,8 +118,16 @@ pub struct ReflectionInvokeData {
pub struct ReflectionClassNewInstData { pub struct ReflectionClassNewInstData {
/// The constructor called by `java.lang.Class.newInstance()` /// The constructor called by `java.lang.Class.newInstance()`
pub constructor: IdMethod, pub constructor: IdMethod,
/// The id of the classloader defining the constructor
pub constructor_cl_id: String,
/// The name of the constructor to call statically.
pub renamed_constructor: Option<IdMethod>,
/// The method calling `java.lang.Class.newInstance()` /// The method calling `java.lang.Class.newInstance()`
pub caller_method: IdMethod, pub caller_method: IdMethod,
/// The id of the classloader defining the caller method
pub caller_cl_id: String,
/// The name of the method that call the method (statically)
pub renamed_caller_method: Option<IdMethod>,
/// Address where the call to `java.lang.Class.newInstance()` was made in `caller_method`. /// Address where the call to `java.lang.Class.newInstance()` was made in `caller_method`.
pub addr: usize, pub addr: usize,
} }
@ -120,8 +138,16 @@ pub struct ReflectionClassNewInstData {
pub struct ReflectionCnstrNewInstData { pub struct ReflectionCnstrNewInstData {
/// The constructor calleb by `java.lang.reflect.Constructor.newInstance()` /// The constructor calleb by `java.lang.reflect.Constructor.newInstance()`
pub constructor: IdMethod, pub constructor: IdMethod,
/// The id of the classloader defining the constructor
pub constructor_cl_id: String,
/// The name of the constructor to call statically.
pub renamed_constructor: Option<IdMethod>,
/// The method calling `java.lang.reflect.Constructor.newInstance()` /// The method calling `java.lang.reflect.Constructor.newInstance()`
pub caller_method: IdMethod, pub caller_method: IdMethod,
/// The id of the classloader defining the caller method
pub caller_cl_id: String,
/// The name of the method that call the method (statically)
pub renamed_caller_method: Option<IdMethod>,
/// Address where the call to `java.lang.Class.newInstance()` was made in `caller_method`. /// Address where the call to `java.lang.Class.newInstance()` was made in `caller_method`.
pub addr: usize, pub addr: usize,
} }