patcher side of things
This commit is contained in:
parent
0a77cf2efb
commit
f8c70b576b
2 changed files with 128 additions and 28 deletions
|
|
@ -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}",
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue