wip compare class laoder
This commit is contained in:
parent
59d6caabd8
commit
1884ff4ac8
7 changed files with 326 additions and 108 deletions
|
|
@ -59,6 +59,8 @@ def on_message(message, data, data_storage: dict, file_storage: Path):
|
||||||
elif message["type"] == "send" and message["payload"]["type"] == "classloader-done":
|
elif message["type"] == "send" and message["payload"]["type"] == "classloader-done":
|
||||||
global CLASSLOADER_DONE
|
global CLASSLOADER_DONE
|
||||||
CLASSLOADER_DONE = True
|
CLASSLOADER_DONE = True
|
||||||
|
elif message["type"] == "send" and message["payload"]["type"] == "app_info":
|
||||||
|
handle_app_info(message["payload"]["data"], data_storage)
|
||||||
else:
|
else:
|
||||||
print("[-] message:", message)
|
print("[-] message:", message)
|
||||||
|
|
||||||
|
|
@ -234,6 +236,14 @@ def handle_load_dex(data, data_storage: dict, file_storage: Path):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_app_info(data, data_storage: dict):
|
||||||
|
data["actualSourceDir"] = data["sourceDir"].removesuffix("/base.apk")
|
||||||
|
data_storage["app_info"] = data
|
||||||
|
print("[+] Received app info:")
|
||||||
|
for k in data.keys():
|
||||||
|
print(f" {k}: {data[k]}")
|
||||||
|
|
||||||
|
|
||||||
def setup_frida(device_name: str, env: dict[str, str], adb: str) -> frida.core.Device:
|
def setup_frida(device_name: str, env: dict[str, str], adb: str) -> frida.core.Device:
|
||||||
if device_name != "":
|
if device_name != "":
|
||||||
device = frida.get_device(device_name)
|
device = frida.get_device(device_name)
|
||||||
|
|
@ -424,7 +434,7 @@ def collect_runtime(
|
||||||
cls = {}
|
cls = {}
|
||||||
for cl in data_storage["classloaders"]:
|
for cl in data_storage["classloaders"]:
|
||||||
# This is verry doubious
|
# This is verry doubious
|
||||||
if cl["cname"] == "dalvik.system.PathClassLoader":
|
if cl["cname"] == "Ldalvik/system/PathClassLoader;":
|
||||||
zip_files = list(
|
zip_files = list(
|
||||||
map(
|
map(
|
||||||
lambda s: s.removeprefix('zip file "').removesuffix('"'),
|
lambda s: s.removeprefix('zip file "').removesuffix('"'),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
const sended_class_loaders = new Set();
|
const sended_class_loaders = new Set();
|
||||||
|
|
||||||
function send_class_loader(cl) {
|
function send_class_loader(cl) {
|
||||||
|
get_app_info();
|
||||||
const System = Java.use('java.lang.System');
|
const System = Java.use('java.lang.System');
|
||||||
let cl_id = System.identityHashCode(cl);
|
let cl_id = System.identityHashCode(cl);
|
||||||
while (cl != null && !sended_class_loaders.has(cl_id)) {
|
while (cl != null && !sended_class_loaders.has(cl_id)) {
|
||||||
|
|
@ -9,7 +10,7 @@ function send_class_loader(cl) {
|
||||||
"id": cl_id,
|
"id": cl_id,
|
||||||
"parent_id": System.identityHashCode(parent_),
|
"parent_id": System.identityHashCode(parent_),
|
||||||
"str": cl.toString(),
|
"str": cl.toString(),
|
||||||
"cname": cl.$className
|
"cname": cl.getClass().descriptorString()
|
||||||
}});
|
}});
|
||||||
sended_class_loaders.add(cl_id);
|
sended_class_loaders.add(cl_id);
|
||||||
cl = parent_;
|
cl = parent_;
|
||||||
|
|
@ -26,6 +27,37 @@ function dump_classloaders() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let info_sent = false
|
||||||
|
function get_app_info() {
|
||||||
|
if (info_sent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var app = Java.use('android.app.ActivityThread').currentApplication();
|
||||||
|
if (app == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var context = app.getApplicationContext();
|
||||||
|
if (context == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var appinfo = context.getApplicationInfo();
|
||||||
|
if (appinfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send({"type": "app_info", "data": {
|
||||||
|
"dataDir": appinfo.dataDir.value,
|
||||||
|
"deviceProtectedDataDir": appinfo.deviceProtectedDataDir.value,
|
||||||
|
"nativeLibraryDir": appinfo.nativeLibraryDir.value,
|
||||||
|
"publicSourceDir": appinfo.publicSourceDir.value,
|
||||||
|
"sharedLibraryFiles": appinfo.sharedLibraryFiles.value,
|
||||||
|
"sourceDir": appinfo.sourceDir.value,
|
||||||
|
"splitNames": appinfo.splitNames.value,
|
||||||
|
"splitPublicSourceDirs": appinfo.splitPublicSourceDirs.value,
|
||||||
|
"splitSourceDirs": appinfo.splitSourceDirs.value,
|
||||||
|
}});
|
||||||
|
info_sent = true;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----- Frida Native Class Loading -----
|
/* ----- Frida Native Class Loading -----
|
||||||
* Broken, for some ineffable frida-android reason.
|
* Broken, for some ineffable frida-android reason.
|
||||||
function registerStackConsumer() {
|
function registerStackConsumer() {
|
||||||
|
|
@ -117,6 +149,8 @@ Java.perform(() => {
|
||||||
const System = Java.use('java.lang.System');
|
const System = Java.use('java.lang.System');
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const StackWalker = Java.use('java.lang.StackWalker');
|
const StackWalker = Java.use('java.lang.StackWalker');
|
||||||
const StackWalkerOptions = Java.use('java.lang.StackWalker$Option');
|
const StackWalkerOptions = Java.use('java.lang.StackWalker$Option');
|
||||||
const StackWalkerOptionsShowHidden = StackWalkerOptions.valueOf("SHOW_HIDDEN_FRAMES");
|
const StackWalkerOptionsShowHidden = StackWalkerOptions.valueOf("SHOW_HIDDEN_FRAMES");
|
||||||
|
|
@ -304,7 +338,8 @@ Java.perform(() => {
|
||||||
let classloader_class = null;
|
let classloader_class = null;
|
||||||
let classloader_id = System.identityHashCode(loader);
|
let classloader_id = System.identityHashCode(loader);
|
||||||
if (loader !== null) {
|
if (loader !== null) {
|
||||||
send_class_loader(loader);
|
// send_class_loader(loader); // Sending names before the end of the initialization
|
||||||
|
// collect the wrong string representation !
|
||||||
classloader_class = loader.getClass().descriptorString();
|
classloader_class = loader.getClass().descriptorString();
|
||||||
}
|
}
|
||||||
send({
|
send({
|
||||||
|
|
|
||||||
|
|
@ -102,15 +102,27 @@ pub(crate) static SCAL_TO_OBJ_DOUBLE: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
pub(crate) static GET_CLASS_LOADER: LazyLock<IdMethod> = LazyLock::new(|| {
|
pub(crate) static GET_CLASS_LOADER: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
IdMethod::from_smali("Ljava/lang/Class;->getClassLoader()Ljava/lang/ClassLoader;").unwrap()
|
IdMethod::from_smali("Ljava/lang/Class;->getClassLoader()Ljava/lang/ClassLoader;").unwrap()
|
||||||
});
|
});
|
||||||
|
pub(crate) static GET_PARENT: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
|
IdMethod::from_smali("Ljava/lang/ClassLoader;->getParent()Ljava/lang/ClassLoader;").unwrap()
|
||||||
|
});
|
||||||
|
pub(crate) static GET_CLASS: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
|
IdMethod::from_smali("Ljava/lang/Object;->getClass()Ljava/lang/Class;").unwrap()
|
||||||
|
});
|
||||||
pub(crate) static TO_STRING: LazyLock<IdMethod> = LazyLock::new(|| {
|
pub(crate) static TO_STRING: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
IdMethod::from_smali("Ljava/lang/Object;->toString()Ljava/lang/String;").unwrap()
|
IdMethod::from_smali("Ljava/lang/Object;->toString()Ljava/lang/String;").unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pub(crate) static BOOT_CLASS_LOADER_TY: LazyLock<IdType> =
|
||||||
|
LazyLock::new(|| IdType::from_smali("Ljava/lang/BootClassLoader;").unwrap());
|
||||||
pub(crate) static OBJECT_TY: LazyLock<IdType> =
|
pub(crate) static OBJECT_TY: LazyLock<IdType> =
|
||||||
LazyLock::new(|| IdType::from_smali("Ljava/lang/Object;").unwrap());
|
LazyLock::new(|| IdType::from_smali("Ljava/lang/Object;").unwrap());
|
||||||
pub(crate) static DELEGATE_LAST_CLASS_LOADER: LazyLock<IdType> =
|
pub(crate) static DELEGATE_LAST_CLASS_LOADER: LazyLock<IdType> =
|
||||||
LazyLock::new(|| IdType::from_smali("Ldalvik/system/DelegateLastClassLoader;").unwrap());
|
LazyLock::new(|| IdType::from_smali("Ldalvik/system/DelegateLastClassLoader;").unwrap());
|
||||||
|
|
||||||
|
pub(crate) static LOG_INFO: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
|
IdMethod::from_smali("Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I").unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
/// Get the method that convert a object to its scalar conterpart (eg `java.lang.Integer` to `int` with
|
/// Get the method that convert a object to its scalar conterpart (eg `java.lang.Integer` to `int` with
|
||||||
/// `Ljava/lang/Integer;->intValue()I`)
|
/// `Ljava/lang/Integer;->intValue()I`)
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ use std::hash::{DefaultHasher, Hash, Hasher};
|
||||||
|
|
||||||
use crate::{dex_types::*, register_manipulation::*, runtime_data::*};
|
use crate::{dex_types::*, register_manipulation::*, runtime_data::*};
|
||||||
|
|
||||||
|
const DEBUG: bool = false;
|
||||||
|
|
||||||
// Interesting stuff: https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/reg_type.h;drc=83db0626fad8c6e0508754fffcbbd58e539d14a5;l=94
|
// Interesting stuff: https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/reg_type.h;drc=83db0626fad8c6e0508754fffcbbd58e539d14a5;l=94
|
||||||
// https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=83db0626fad8c6e0508754fffcbbd58e539d14a5;l=5328
|
// https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=83db0626fad8c6e0508754fffcbbd58e539d14a5;l=5328
|
||||||
/// `meth`: the method that make reflectif calls. This is the method to patch.
|
/// `meth`: the method that make reflectif calls. This is the method to patch.
|
||||||
|
|
@ -340,11 +342,12 @@ fn gen_tester_method(
|
||||||
tester_methods_class: IdType,
|
tester_methods_class: IdType,
|
||||||
method_to_test: IdMethod,
|
method_to_test: IdMethod,
|
||||||
is_constructor: bool,
|
is_constructor: bool,
|
||||||
classloader: Option<&ClassLoaderData>,
|
classloader: Option<String>,
|
||||||
|
classloaders: &HashMap<String, ClassLoaderData>,
|
||||||
) -> Result<Method> {
|
) -> Result<Method> {
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
if let Some(classloader) = classloader {
|
if let Some(ref id) = classloader {
|
||||||
classloader.id.hash(&mut hasher);
|
id.hash(&mut hasher);
|
||||||
} else {
|
} else {
|
||||||
"00000000".hash(&mut hasher);
|
"00000000".hash(&mut hasher);
|
||||||
}
|
}
|
||||||
|
|
@ -363,8 +366,9 @@ fn gen_tester_method(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let method_test_name = format!("check_is_{c_name}_{m_name}_{hash:016x}");
|
||||||
let descriptor = IdMethod::new(
|
let descriptor = IdMethod::new(
|
||||||
format!("check_is_{c_name}_{m_name}_{hash:016x}").into(),
|
method_test_name.as_str().into(),
|
||||||
IdMethodType::new(
|
IdMethodType::new(
|
||||||
IdType::boolean(),
|
IdType::boolean(),
|
||||||
vec![if is_constructor {
|
vec![if is_constructor {
|
||||||
|
|
@ -379,8 +383,11 @@ fn gen_tester_method(
|
||||||
let no_label: String = "lable_no".into();
|
let no_label: String = "lable_no".into();
|
||||||
let reg_arr = 0;
|
let reg_arr = 0;
|
||||||
let reg_arr_idx = 1;
|
let reg_arr_idx = 1;
|
||||||
let reg_arr_val = 2;
|
let reg_tst_val = 2;
|
||||||
let reg_ref_method = 3;
|
let reg_def_type = 3;
|
||||||
|
let reg_cmp_val = 4;
|
||||||
|
let reg_class_loader = 5;
|
||||||
|
let reg_ref_method = 6;
|
||||||
// Check for arg type
|
// Check for arg type
|
||||||
let mut insns = if !is_constructor {
|
let mut insns = if !is_constructor {
|
||||||
vec![
|
vec![
|
||||||
|
|
@ -407,12 +414,12 @@ fn gen_tester_method(
|
||||||
arr: reg_arr,
|
arr: reg_arr,
|
||||||
},
|
},
|
||||||
Instruction::Const {
|
Instruction::Const {
|
||||||
reg: reg_arr_val,
|
reg: reg_tst_val,
|
||||||
lit: method_to_test.proto.get_parameters().len() as i32,
|
lit: method_to_test.proto.get_parameters().len() as i32,
|
||||||
},
|
},
|
||||||
Instruction::IfNe {
|
Instruction::IfNe {
|
||||||
a: reg_arr_idx,
|
a: reg_arr_idx,
|
||||||
b: reg_arr_val,
|
b: reg_tst_val,
|
||||||
label: no_label.clone(),
|
label: no_label.clone(),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
@ -428,40 +435,38 @@ fn gen_tester_method(
|
||||||
lit: i as i32,
|
lit: i as i32,
|
||||||
});
|
});
|
||||||
insns.push(Instruction::AGetObject {
|
insns.push(Instruction::AGetObject {
|
||||||
dest: reg_arr_val,
|
dest: reg_tst_val,
|
||||||
arr: reg_arr,
|
arr: reg_arr,
|
||||||
idx: reg_arr_idx,
|
idx: reg_arr_idx,
|
||||||
});
|
});
|
||||||
insns.push(Instruction::ConstClass {
|
insns.push(Instruction::ConstClass {
|
||||||
reg: reg_arr_idx, // wrong name, but available for tmp val
|
reg: reg_cmp_val,
|
||||||
lit: param,
|
lit: param,
|
||||||
});
|
});
|
||||||
insns.push(Instruction::InvokeVirtual {
|
insns.push(Instruction::InvokeVirtual {
|
||||||
method: CLT_GET_DESCR_STRING.clone(),
|
method: CLT_GET_DESCR_STRING.clone(),
|
||||||
args: vec![reg_arr_idx as u16],
|
args: vec![reg_cmp_val as u16],
|
||||||
});
|
});
|
||||||
insns.push(Instruction::MoveResultObject { to: reg_arr_idx });
|
insns.push(Instruction::MoveResultObject { to: reg_cmp_val });
|
||||||
insns.push(Instruction::InvokeVirtual {
|
insns.push(Instruction::InvokeVirtual {
|
||||||
method: CLT_GET_DESCR_STRING.clone(),
|
method: CLT_GET_DESCR_STRING.clone(),
|
||||||
args: vec![reg_arr_val as u16],
|
args: vec![reg_tst_val as u16],
|
||||||
});
|
});
|
||||||
insns.push(Instruction::MoveResultObject { to: reg_arr_val });
|
insns.push(Instruction::MoveResultObject { to: reg_tst_val });
|
||||||
insns.push(Instruction::InvokeVirtual {
|
insns.push(Instruction::InvokeVirtual {
|
||||||
method: STR_EQ.clone(),
|
method: STR_EQ.clone(),
|
||||||
args: vec![reg_arr_idx as u16, reg_arr_val as u16],
|
args: vec![reg_cmp_val as u16, reg_tst_val as u16],
|
||||||
});
|
|
||||||
insns.push(Instruction::MoveResult {
|
|
||||||
to: reg_arr_idx, // wrong name, but available for tmp val
|
|
||||||
});
|
});
|
||||||
|
insns.push(Instruction::MoveResult { to: reg_cmp_val });
|
||||||
insns.push(Instruction::IfEqZ {
|
insns.push(Instruction::IfEqZ {
|
||||||
a: reg_arr_idx,
|
a: reg_cmp_val,
|
||||||
label: no_label.clone(),
|
label: no_label.clone(),
|
||||||
});
|
});
|
||||||
// Comparing Type does not work when different types share the same name (eg type from
|
// Comparing Type does not work when different types share the same name (eg type from
|
||||||
// another class loader)
|
// another class loader)
|
||||||
//insns.push(Instruction::IfNe {
|
//insns.push(Instruction::IfNe {
|
||||||
// a: reg_arr_idx,
|
// a: reg_arr_idx,
|
||||||
// b: reg_arr_val,
|
// b: reg_tst_val,
|
||||||
// label: no_label.clone(),
|
// label: no_label.clone(),
|
||||||
//})
|
//})
|
||||||
}
|
}
|
||||||
|
|
@ -473,22 +478,18 @@ fn gen_tester_method(
|
||||||
method: MTH_GET_NAME.clone(),
|
method: MTH_GET_NAME.clone(),
|
||||||
args: vec![reg_ref_method],
|
args: vec![reg_ref_method],
|
||||||
},
|
},
|
||||||
Instruction::MoveResultObject {
|
Instruction::MoveResultObject { to: reg_tst_val },
|
||||||
to: reg_arr_idx, // wrong name, but available for tmp val
|
|
||||||
},
|
|
||||||
Instruction::ConstString {
|
Instruction::ConstString {
|
||||||
reg: reg_arr_val, // wrong name, but available for tmp val
|
reg: reg_cmp_val,
|
||||||
lit: method_to_test.name.clone(),
|
lit: method_to_test.name.clone(),
|
||||||
},
|
},
|
||||||
Instruction::InvokeVirtual {
|
Instruction::InvokeVirtual {
|
||||||
method: STR_EQ.clone(),
|
method: STR_EQ.clone(),
|
||||||
args: vec![reg_arr_idx as u16, reg_arr_val as u16],
|
args: vec![reg_tst_val as u16, reg_cmp_val as u16],
|
||||||
},
|
|
||||||
Instruction::MoveResult {
|
|
||||||
to: reg_arr_idx, // wrong name, but available for tmp val
|
|
||||||
},
|
},
|
||||||
|
Instruction::MoveResult { to: reg_cmp_val },
|
||||||
Instruction::IfEqZ {
|
Instruction::IfEqZ {
|
||||||
a: reg_arr_idx,
|
a: reg_cmp_val,
|
||||||
label: no_label.clone(),
|
label: no_label.clone(),
|
||||||
},
|
},
|
||||||
// Check Return Type
|
// Check Return Type
|
||||||
|
|
@ -496,39 +497,35 @@ fn gen_tester_method(
|
||||||
method: MTH_GET_RET_TY.clone(),
|
method: MTH_GET_RET_TY.clone(),
|
||||||
args: vec![reg_ref_method],
|
args: vec![reg_ref_method],
|
||||||
},
|
},
|
||||||
Instruction::MoveResultObject {
|
Instruction::MoveResultObject { to: reg_tst_val },
|
||||||
to: reg_arr_idx, // wrong name, but available for tmp val
|
Instruction::InvokeVirtual {
|
||||||
|
method: CLT_GET_DESCR_STRING.clone(),
|
||||||
|
args: vec![reg_tst_val as u16],
|
||||||
},
|
},
|
||||||
|
Instruction::MoveResultObject { to: reg_tst_val },
|
||||||
Instruction::ConstClass {
|
Instruction::ConstClass {
|
||||||
reg: reg_arr_val, // wrong name, but available for tmp val
|
reg: reg_cmp_val,
|
||||||
lit: method_to_test.proto.get_return_type(),
|
lit: method_to_test.proto.get_return_type(),
|
||||||
},
|
},
|
||||||
Instruction::InvokeVirtual {
|
Instruction::InvokeVirtual {
|
||||||
method: CLT_GET_DESCR_STRING.clone(),
|
method: CLT_GET_DESCR_STRING.clone(),
|
||||||
args: vec![reg_arr_idx as u16],
|
args: vec![reg_cmp_val as u16],
|
||||||
},
|
},
|
||||||
Instruction::MoveResultObject { to: reg_arr_idx },
|
Instruction::MoveResultObject { to: reg_cmp_val },
|
||||||
Instruction::InvokeVirtual {
|
|
||||||
method: CLT_GET_DESCR_STRING.clone(),
|
|
||||||
args: vec![reg_arr_val as u16],
|
|
||||||
},
|
|
||||||
Instruction::MoveResultObject { to: reg_arr_val },
|
|
||||||
Instruction::InvokeVirtual {
|
Instruction::InvokeVirtual {
|
||||||
method: STR_EQ.clone(),
|
method: STR_EQ.clone(),
|
||||||
args: vec![reg_arr_idx as u16, reg_arr_val as u16],
|
args: vec![reg_cmp_val as u16, reg_tst_val as u16],
|
||||||
},
|
|
||||||
Instruction::MoveResult {
|
|
||||||
to: reg_arr_idx, // wrong name, but available for tmp val
|
|
||||||
},
|
},
|
||||||
|
Instruction::MoveResult { to: reg_cmp_val },
|
||||||
Instruction::IfEqZ {
|
Instruction::IfEqZ {
|
||||||
a: reg_arr_idx,
|
a: reg_cmp_val,
|
||||||
label: no_label.clone(),
|
label: no_label.clone(),
|
||||||
},
|
},
|
||||||
// Comparing Type does not work when different types share the same name (eg type from
|
// Comparing Type does not work when different types share the same name (eg type from
|
||||||
// another class loader)
|
// another class loader)
|
||||||
//Instruction::IfNe {
|
//Instruction::IfNe {
|
||||||
// a: reg_arr_idx,
|
// a: reg_arr_idx,
|
||||||
// b: reg_arr_val,
|
// b: reg_tst_val,
|
||||||
// label: no_label.clone(),
|
// label: no_label.clone(),
|
||||||
//},
|
//},
|
||||||
]);
|
]);
|
||||||
|
|
@ -545,109 +542,211 @@ fn gen_tester_method(
|
||||||
args: vec![reg_ref_method],
|
args: vec![reg_ref_method],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
insns.push(Instruction::MoveResultObject { to: reg_def_type });
|
||||||
|
|
||||||
//Check the classloader
|
//Check the classloader
|
||||||
if let Some(classloader) = classloader {
|
let mut current_classloader = classloader.as_ref().and_then(|id| classloaders.get(id));
|
||||||
|
let check_class_loader = current_classloader.is_some();
|
||||||
|
if check_class_loader {
|
||||||
insns.append(&mut vec![
|
insns.append(&mut vec![
|
||||||
// Get the string representation of the classloader.
|
// Get the string representation of the classloader.
|
||||||
// Not the ideal, but best cross execution classloader identifier we have.
|
// Not the ideal, but best cross execution classloader identifier we have.
|
||||||
Instruction::MoveResultObject {
|
|
||||||
to: reg_arr_idx, // wrong name, but available for tmp val
|
|
||||||
},
|
|
||||||
Instruction::InvokeVirtual {
|
Instruction::InvokeVirtual {
|
||||||
method: GET_CLASS_LOADER.clone(),
|
method: GET_CLASS_LOADER.clone(),
|
||||||
args: vec![reg_arr_idx as u16],
|
args: vec![reg_def_type as u16],
|
||||||
},
|
|
||||||
Instruction::MoveResultObject { to: reg_arr_idx },
|
|
||||||
Instruction::InvokeVirtual {
|
|
||||||
method: TO_STRING.clone(),
|
|
||||||
args: vec![reg_arr_idx as u16],
|
|
||||||
},
|
},
|
||||||
Instruction::MoveResultObject {
|
Instruction::MoveResultObject {
|
||||||
to: reg_arr_idx, // wrong name, but available for tmp val
|
to: reg_class_loader,
|
||||||
},
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
while let Some(classloader) = current_classloader {
|
||||||
|
if classloader.cname == *BOOT_CLASS_LOADER_TY {
|
||||||
|
// Ljava/lang/BootClassLoader; is complicated.
|
||||||
|
// It's string rep is "java.lang.BootClassLoader@7e2aeab" where "7e2aeab" is it's
|
||||||
|
// runtime hash id: the name change at each run. We need to compare with its type (it's
|
||||||
|
// ok, it's supposed to be a singleton).
|
||||||
|
// Also, it can be represented at runtime by the null pointer, so we need to accept the
|
||||||
|
// null pointer as a valid value.
|
||||||
|
insns.append(&mut vec![
|
||||||
|
Instruction::IfEqZ {
|
||||||
|
a: reg_class_loader,
|
||||||
|
label: "label_end_classloader_test".into(),
|
||||||
|
},
|
||||||
|
Instruction::InvokeVirtual {
|
||||||
|
method: GET_CLASS.clone(),
|
||||||
|
args: vec![reg_class_loader as u16],
|
||||||
|
},
|
||||||
|
Instruction::MoveResultObject { to: reg_tst_val },
|
||||||
|
Instruction::InvokeVirtual {
|
||||||
|
method: CLT_GET_DESCR_STRING.clone(),
|
||||||
|
args: vec![reg_tst_val as u16],
|
||||||
|
},
|
||||||
|
Instruction::MoveResultObject { to: reg_tst_val },
|
||||||
|
Instruction::ConstClass {
|
||||||
|
reg: reg_cmp_val,
|
||||||
|
lit: BOOT_CLASS_LOADER_TY.clone(),
|
||||||
|
},
|
||||||
|
Instruction::InvokeVirtual {
|
||||||
|
method: CLT_GET_DESCR_STRING.clone(),
|
||||||
|
args: vec![reg_cmp_val as u16],
|
||||||
|
},
|
||||||
|
Instruction::MoveResultObject { to: reg_cmp_val },
|
||||||
|
Instruction::InvokeVirtual {
|
||||||
|
method: STR_EQ.clone(),
|
||||||
|
args: vec![reg_cmp_val as u16, reg_tst_val as u16],
|
||||||
|
},
|
||||||
|
Instruction::MoveResult { to: reg_cmp_val },
|
||||||
|
Instruction::IfEqZ {
|
||||||
|
a: reg_cmp_val,
|
||||||
|
label: no_label.clone(),
|
||||||
|
},
|
||||||
|
Instruction::Label {
|
||||||
|
name: "label_end_classloader_test".into(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
insns.append(&mut vec![
|
||||||
|
Instruction::IfEqZ {
|
||||||
|
a: reg_class_loader,
|
||||||
|
label: no_label.clone(),
|
||||||
|
},
|
||||||
|
Instruction::InvokeVirtual {
|
||||||
|
method: TO_STRING.clone(),
|
||||||
|
args: vec![reg_class_loader as u16],
|
||||||
|
},
|
||||||
|
Instruction::MoveResultObject { to: reg_tst_val },
|
||||||
Instruction::ConstString {
|
Instruction::ConstString {
|
||||||
reg: reg_arr_val,
|
reg: reg_cmp_val,
|
||||||
lit: classloader.string_representation.as_str().into(),
|
lit: classloader.string_representation.as_str().into(),
|
||||||
},
|
},
|
||||||
Instruction::InvokeVirtual {
|
Instruction::InvokeVirtual {
|
||||||
method: STR_EQ.clone(),
|
method: STR_EQ.clone(),
|
||||||
args: vec![reg_arr_idx as u16, reg_arr_val as u16],
|
args: vec![reg_cmp_val as u16, reg_tst_val as u16],
|
||||||
},
|
|
||||||
Instruction::MoveResult {
|
|
||||||
to: reg_arr_idx, // wrong name, but available for tmp val
|
|
||||||
},
|
},
|
||||||
|
Instruction::MoveResult { to: reg_cmp_val },
|
||||||
Instruction::IfEqZ {
|
Instruction::IfEqZ {
|
||||||
a: reg_arr_idx,
|
a: reg_cmp_val,
|
||||||
label: no_label.clone(),
|
label: no_label.clone(),
|
||||||
},
|
},
|
||||||
|
Instruction::InvokeVirtual {
|
||||||
|
method: GET_PARENT.clone(),
|
||||||
|
args: vec![reg_class_loader as u16],
|
||||||
|
},
|
||||||
|
Instruction::MoveResultObject {
|
||||||
|
to: reg_class_loader,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
// Get Declaring Type
|
let parent_id = classloader.parent_id.clone();
|
||||||
if is_constructor {
|
// If parent_id is None, the parent is in fact the boot class loader (except for the
|
||||||
insns.push(Instruction::InvokeVirtual {
|
// boot class loader itself, already handled at the start of the loop).
|
||||||
method: CNSTR_GET_DEC_CLS.clone(),
|
current_classloader = if let Some(ref id) = parent_id {
|
||||||
args: vec![reg_ref_method],
|
classloaders.get(id)
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
insns.push(Instruction::InvokeVirtual {
|
classloaders
|
||||||
method: MTH_GET_DEC_CLS.clone(),
|
.values()
|
||||||
args: vec![reg_ref_method],
|
.find(|cl| cl.cname == *BOOT_CLASS_LOADER_TY)
|
||||||
});
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Declaring Type
|
// Check Declaring Type
|
||||||
insns.append(&mut vec![
|
insns.append(&mut vec![
|
||||||
Instruction::MoveResultObject {
|
|
||||||
to: reg_arr_idx, // wrong name, but available for tmp val
|
|
||||||
},
|
|
||||||
Instruction::ConstClass {
|
Instruction::ConstClass {
|
||||||
reg: reg_arr_val, // wrong name, but available for tmp val
|
reg: reg_cmp_val,
|
||||||
lit: method_to_test.class_.clone(),
|
lit: method_to_test.class_.clone(),
|
||||||
},
|
},
|
||||||
Instruction::InvokeVirtual {
|
Instruction::InvokeVirtual {
|
||||||
method: CLT_GET_DESCR_STRING.clone(),
|
method: CLT_GET_DESCR_STRING.clone(),
|
||||||
args: vec![reg_arr_idx as u16],
|
args: vec![reg_cmp_val as u16],
|
||||||
},
|
},
|
||||||
Instruction::MoveResultObject { to: reg_arr_idx },
|
Instruction::MoveResultObject { to: reg_cmp_val },
|
||||||
Instruction::InvokeVirtual {
|
Instruction::InvokeVirtual {
|
||||||
method: CLT_GET_DESCR_STRING.clone(),
|
method: CLT_GET_DESCR_STRING.clone(),
|
||||||
args: vec![reg_arr_val as u16],
|
args: vec![reg_def_type as u16],
|
||||||
},
|
},
|
||||||
Instruction::MoveResultObject { to: reg_arr_val },
|
Instruction::MoveResultObject { to: reg_tst_val },
|
||||||
Instruction::InvokeVirtual {
|
Instruction::InvokeVirtual {
|
||||||
method: STR_EQ.clone(),
|
method: STR_EQ.clone(),
|
||||||
args: vec![reg_arr_idx as u16, reg_arr_val as u16],
|
args: vec![reg_cmp_val as u16, reg_tst_val as u16],
|
||||||
},
|
|
||||||
Instruction::MoveResult {
|
|
||||||
to: reg_arr_idx, // wrong name, but available for tmp val
|
|
||||||
},
|
},
|
||||||
|
Instruction::MoveResult { to: reg_cmp_val },
|
||||||
Instruction::IfEqZ {
|
Instruction::IfEqZ {
|
||||||
a: reg_arr_idx,
|
a: reg_cmp_val,
|
||||||
label: no_label.clone(),
|
label: no_label.clone(),
|
||||||
},
|
},
|
||||||
// Comparing Type does not work when different types share the same name (eg type from
|
// Comparing Type does not work when different types share the same name (eg type from
|
||||||
// another class loader)
|
// another class loader)
|
||||||
//Instruction::IfNe {
|
//Instruction::IfNe {
|
||||||
// a: reg_arr_idx,
|
// a: reg_arr_idx,
|
||||||
// b: reg_arr_val,
|
// b: reg_tst_val,
|
||||||
// label: no_label.clone(),
|
// label: no_label.clone(),
|
||||||
//},
|
//},
|
||||||
|
]);
|
||||||
|
if DEBUG {
|
||||||
|
insns.append(&mut vec![
|
||||||
|
Instruction::ConstString {
|
||||||
|
reg: reg_tst_val,
|
||||||
|
lit: "THESEUS".into(),
|
||||||
|
},
|
||||||
|
Instruction::ConstString {
|
||||||
|
reg: reg_cmp_val,
|
||||||
|
lit: format!(
|
||||||
|
"T.{method_test_name}() (test of {}) returned true",
|
||||||
|
method_to_test
|
||||||
|
.try_to_smali()
|
||||||
|
.unwrap_or("failed to convert".into())
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
},
|
||||||
|
Instruction::InvokeStatic {
|
||||||
|
method: LOG_INFO.clone(),
|
||||||
|
args: vec![reg_tst_val as u16, reg_cmp_val as u16],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
insns.append(&mut vec![
|
||||||
Instruction::Const {
|
Instruction::Const {
|
||||||
reg: reg_arr_val,
|
reg: reg_cmp_val,
|
||||||
lit: 1,
|
lit: 1,
|
||||||
},
|
},
|
||||||
Instruction::Return { reg: reg_arr_val },
|
Instruction::Return { reg: reg_cmp_val },
|
||||||
Instruction::Label { name: no_label },
|
Instruction::Label { name: no_label },
|
||||||
|
]);
|
||||||
|
if DEBUG {
|
||||||
|
insns.append(&mut vec![
|
||||||
|
Instruction::ConstString {
|
||||||
|
reg: reg_tst_val,
|
||||||
|
lit: "THESEUS".into(),
|
||||||
|
},
|
||||||
|
Instruction::ConstString {
|
||||||
|
reg: reg_cmp_val,
|
||||||
|
lit: format!(
|
||||||
|
"T.{method_test_name}() (test of {}) returned false",
|
||||||
|
method_to_test
|
||||||
|
.try_to_smali()
|
||||||
|
.unwrap_or("failed to convert".into())
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
},
|
||||||
|
Instruction::InvokeStatic {
|
||||||
|
method: LOG_INFO.clone(),
|
||||||
|
args: vec![reg_tst_val as u16, reg_cmp_val as u16],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
insns.append(&mut vec![
|
||||||
Instruction::Const {
|
Instruction::Const {
|
||||||
reg: reg_arr_val,
|
reg: reg_cmp_val,
|
||||||
lit: 0,
|
lit: 0,
|
||||||
},
|
},
|
||||||
Instruction::Return { reg: reg_arr_val },
|
Instruction::Return { reg: reg_cmp_val },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
method.is_static = true;
|
method.is_static = true;
|
||||||
method.is_final = true;
|
method.is_final = true;
|
||||||
method.code = Some(Code::new(
|
method.code = Some(Code::new(
|
||||||
4, //registers_size, 3 reg + 1 parameter reg
|
7, //registers_size, 6 reg + 1 parameter reg
|
||||||
insns,
|
insns,
|
||||||
Some(vec![Some("meth".into())]), // parameter_names
|
Some(vec![Some("meth".into())]), // parameter_names
|
||||||
));
|
));
|
||||||
|
|
@ -667,6 +766,7 @@ fn gen_tester_method(
|
||||||
/// reflected method. If None, the classloader is not tested. Platform classes should probably
|
/// reflected method. If None, the classloader is not tested. Platform classes should probably
|
||||||
/// not be tested (the bootclassloader can be represented with a null reference, which may
|
/// not be tested (the bootclassloader can be represented with a null reference, which may
|
||||||
/// lead to a null pointer exception).
|
/// lead to a null pointer exception).
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn test_method(
|
fn test_method(
|
||||||
method_obj_reg: u16,
|
method_obj_reg: u16,
|
||||||
id_method: IdMethod,
|
id_method: IdMethod,
|
||||||
|
|
@ -674,7 +774,8 @@ fn test_method(
|
||||||
reg_inf: &mut RegistersInfo,
|
reg_inf: &mut RegistersInfo,
|
||||||
tester_methods_class: IdType,
|
tester_methods_class: IdType,
|
||||||
tester_methods: &mut HashMap<IdMethod, Method>,
|
tester_methods: &mut HashMap<IdMethod, Method>,
|
||||||
classloader: Option<&ClassLoaderData>,
|
classloader: Option<String>,
|
||||||
|
classloaders: &HashMap<String, ClassLoaderData>,
|
||||||
) -> Result<Vec<Instruction>> {
|
) -> Result<Vec<Instruction>> {
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
let tst_descriptor = match tester_methods.entry(id_method.clone()) {
|
let tst_descriptor = match tester_methods.entry(id_method.clone()) {
|
||||||
|
|
@ -684,6 +785,7 @@ fn test_method(
|
||||||
id_method,
|
id_method,
|
||||||
false,
|
false,
|
||||||
classloader,
|
classloader,
|
||||||
|
classloaders,
|
||||||
)?),
|
)?),
|
||||||
}
|
}
|
||||||
.descriptor
|
.descriptor
|
||||||
|
|
@ -765,7 +867,7 @@ fn get_invoke_block(
|
||||||
let classloader = if ref_data.method.class_.is_platform_class() {
|
let classloader = if ref_data.method.class_.is_platform_class() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
classloaders.get(&ref_data.method_cl_id)
|
Some(ref_data.method_cl_id.clone())
|
||||||
};
|
};
|
||||||
let mut insns = test_method(
|
let mut insns = test_method(
|
||||||
method_obj,
|
method_obj,
|
||||||
|
|
@ -775,6 +877,7 @@ fn get_invoke_block(
|
||||||
tester_methods_class,
|
tester_methods_class,
|
||||||
tester_methods,
|
tester_methods,
|
||||||
classloader,
|
classloader,
|
||||||
|
classloaders,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !ref_data.is_static {
|
if !ref_data.is_static {
|
||||||
|
|
@ -979,7 +1082,7 @@ fn get_cnstr_new_inst_block(
|
||||||
let classloader = if ref_data.constructor.class_.is_platform_class() {
|
let classloader = if ref_data.constructor.class_.is_platform_class() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
classloaders.get(&ref_data.constructor_cl_id)
|
Some(ref_data.constructor_cl_id.clone())
|
||||||
};
|
};
|
||||||
let mut insns = test_cnstr(
|
let mut insns = test_cnstr(
|
||||||
cnst_reg,
|
cnst_reg,
|
||||||
|
|
@ -989,6 +1092,7 @@ fn get_cnstr_new_inst_block(
|
||||||
tester_methods_class,
|
tester_methods_class,
|
||||||
tester_methods,
|
tester_methods,
|
||||||
classloader,
|
classloader,
|
||||||
|
classloaders,
|
||||||
)?;
|
)?;
|
||||||
insns.append(&mut get_args_from_obj_arr(
|
insns.append(&mut get_args_from_obj_arr(
|
||||||
&ref_data.constructor.proto.get_parameters(), // TODO: what if args are renammed?
|
&ref_data.constructor.proto.get_parameters(), // TODO: what if args are renammed?
|
||||||
|
|
@ -1039,6 +1143,7 @@ fn get_cnstr_new_inst_block(
|
||||||
/// - `classloader`: is the runtime data of the classloader that loaded the. If None, the classloader
|
/// - `classloader`: is the runtime data of the classloader that loaded the. If None, the classloader
|
||||||
/// is not tested. Platform classes should probably not be tested (the bootclassloader can be
|
/// is not tested. Platform classes should probably not be tested (the bootclassloader can be
|
||||||
/// represented with a null reference, which may lead to a null pointer exception).
|
/// represented with a null reference, which may lead to a null pointer exception).
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn test_cnstr(
|
fn test_cnstr(
|
||||||
cnst_reg: u16,
|
cnst_reg: u16,
|
||||||
id_method: IdMethod,
|
id_method: IdMethod,
|
||||||
|
|
@ -1046,7 +1151,8 @@ fn test_cnstr(
|
||||||
reg_inf: &mut RegistersInfo,
|
reg_inf: &mut RegistersInfo,
|
||||||
tester_methods_class: IdType,
|
tester_methods_class: IdType,
|
||||||
tester_methods: &mut HashMap<IdMethod, Method>,
|
tester_methods: &mut HashMap<IdMethod, Method>,
|
||||||
classloader: Option<&ClassLoaderData>,
|
classloader: Option<String>,
|
||||||
|
classloaders: &HashMap<String, ClassLoaderData>,
|
||||||
) -> Result<Vec<Instruction>> {
|
) -> Result<Vec<Instruction>> {
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
let tst_descriptor = match tester_methods.entry(id_method.clone()) {
|
let tst_descriptor = match tester_methods.entry(id_method.clone()) {
|
||||||
|
|
@ -1056,6 +1162,7 @@ fn test_cnstr(
|
||||||
id_method,
|
id_method,
|
||||||
true,
|
true,
|
||||||
classloader,
|
classloader,
|
||||||
|
classloaders,
|
||||||
)?),
|
)?),
|
||||||
}
|
}
|
||||||
.descriptor
|
.descriptor
|
||||||
|
|
|
||||||
|
|
@ -210,5 +210,5 @@ pub struct ClassLoaderData {
|
||||||
#[serde(rename = "str")]
|
#[serde(rename = "str")]
|
||||||
pub string_representation: String,
|
pub string_representation: String,
|
||||||
/// The class of the class loader.
|
/// The class of the class loader.
|
||||||
pub cname: String, // TODO: IdType,
|
pub cname: IdType,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,14 @@ import java.lang.reflect.Method;
|
||||||
import com.example.theseus.Utils;
|
import com.example.theseus.Utils;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
static ClassLoader delegateLastClassLoaderParent = null;
|
||||||
|
static ClassLoader delegateLastClassLoader = null;
|
||||||
|
static ClassLoader dexClassLoaderParent = null;
|
||||||
|
static ClassLoader dexClassLoader = null;
|
||||||
|
static ClassLoader inMemoryDexClassLoaderParent = null;
|
||||||
|
static ClassLoader inMemoryDexClassLoader = null;
|
||||||
|
static ClassLoader pathClassLoaderParent = null;
|
||||||
|
static ClassLoader pathClassLoader = null;
|
||||||
|
|
||||||
public static String getdexfile(Activity ac, String name) throws Exception {
|
public static String getdexfile(Activity ac, String name) throws Exception {
|
||||||
File dexfile = new File(ac.getCacheDir(), name);
|
File dexfile = new File(ac.getCacheDir(), name);
|
||||||
|
|
@ -30,25 +38,65 @@ public class Main {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void run(Activity ac, String clname, boolean hasCollision, boolean hasParent, String methodType) {
|
public static void run(Activity ac, String clname, boolean hasCollision, boolean hasParent, String methodType) {
|
||||||
|
ClassLoader cl = Main.class.getClassLoader();
|
||||||
|
ClassLoader parent;
|
||||||
try {
|
try {
|
||||||
Log.i("THESEUS", "clname: " + clname + ", hasCollision: " + hasCollision + ", hasParent: " + hasParent + ", methodType: " + methodType);
|
Log.i("THESEUS", "clname: " + clname + ", hasCollision: " + hasCollision + ", hasParent: " + hasParent + ", methodType: " + methodType);
|
||||||
ClassLoader cl;
|
String name = "a.dex";
|
||||||
ClassLoader parent;
|
|
||||||
if (hasParent) {
|
if (hasParent) {
|
||||||
parent = Main.class.getClassLoader();
|
parent = Main.class.getClassLoader();
|
||||||
|
name = "b.dex";
|
||||||
} else {
|
} else {
|
||||||
parent = null;
|
parent = null;
|
||||||
}
|
}
|
||||||
if (clname.equals("DelegateLastClassLoader")) {
|
if (clname.equals("DelegateLastClassLoader")) {
|
||||||
cl = new DelegateLastClassLoader(getdexfile(ac, "a.dex"), parent);
|
if (parent == null) {
|
||||||
|
if (delegateLastClassLoader == null) {
|
||||||
|
delegateLastClassLoader = new DelegateLastClassLoader(getdexfile(ac, name), parent);
|
||||||
|
}
|
||||||
|
cl = delegateLastClassLoader;
|
||||||
|
} else {
|
||||||
|
if (delegateLastClassLoaderParent == null) {
|
||||||
|
delegateLastClassLoaderParent = new DelegateLastClassLoader(getdexfile(ac, name), parent);
|
||||||
|
}
|
||||||
|
cl = delegateLastClassLoaderParent;
|
||||||
|
}
|
||||||
} else if (clname.equals("DexClassLoader")) {
|
} else if (clname.equals("DexClassLoader")) {
|
||||||
cl = new DexClassLoader(getdexfile(ac, "a.dex"), null, null, parent);
|
if (parent == null) {
|
||||||
|
if (dexClassLoader == null) {
|
||||||
|
dexClassLoader = new DexClassLoader(getdexfile(ac, name), null, null, parent);
|
||||||
|
}
|
||||||
|
cl = dexClassLoader;
|
||||||
|
} else {
|
||||||
|
if (dexClassLoaderParent == null) {
|
||||||
|
dexClassLoaderParent = new DexClassLoader(getdexfile(ac, name), null, null, parent);
|
||||||
|
}
|
||||||
|
cl = dexClassLoaderParent;
|
||||||
|
}
|
||||||
} else if (clname.equals("InMemoryDexClassLoader")) {
|
} else if (clname.equals("InMemoryDexClassLoader")) {
|
||||||
cl = new InMemoryDexClassLoader(getdexbuffer(ac, "a.dex"), parent);
|
if (parent == null) {
|
||||||
|
if (inMemoryDexClassLoader == null) {
|
||||||
|
inMemoryDexClassLoader = new InMemoryDexClassLoader(getdexbuffer(ac, name), parent);
|
||||||
|
}
|
||||||
|
cl = inMemoryDexClassLoader;
|
||||||
|
} else {
|
||||||
|
if (inMemoryDexClassLoaderParent == null) {
|
||||||
|
inMemoryDexClassLoaderParent = new InMemoryDexClassLoader(getdexbuffer(ac, name), parent);
|
||||||
|
}
|
||||||
|
cl = inMemoryDexClassLoaderParent;
|
||||||
|
}
|
||||||
} else if (clname.equals("PathClassLoader")) {
|
} else if (clname.equals("PathClassLoader")) {
|
||||||
cl = new PathClassLoader(getdexfile(ac, "a.dex"), parent);
|
if (parent == null) {
|
||||||
} else {
|
if (pathClassLoader == null) {
|
||||||
cl = Main.class.getClassLoader();
|
pathClassLoader = new PathClassLoader(getdexfile(ac, name), parent);
|
||||||
|
}
|
||||||
|
cl = pathClassLoader;
|
||||||
|
} else {
|
||||||
|
if (pathClassLoaderParent == null) {
|
||||||
|
pathClassLoaderParent = new PathClassLoader(getdexfile(ac, name), parent);
|
||||||
|
}
|
||||||
|
cl = pathClassLoaderParent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Class clz = null;
|
Class clz = null;
|
||||||
|
|
@ -194,6 +242,7 @@ public class Main {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Log.e("THESEUS", "class loader name: " + cl.toString());
|
||||||
Log.e("THESEUS", "error:", e);
|
Log.e("THESEUS", "error:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,11 @@ public class MainActivity extends Activity {
|
||||||
out = new FileOutputStream(outFile);
|
out = new FileOutputStream(outFile);
|
||||||
Utils.copy(in, out);
|
Utils.copy(in, out);
|
||||||
outFile.renameTo(new File(getCacheDir(), "a.dex")); // security?
|
outFile.renameTo(new File(getCacheDir(), "a.dex")); // security?
|
||||||
|
in = assetManager.open("a.dex");
|
||||||
|
outFile = new File(getCacheDir(), "b.dex_");
|
||||||
|
out = new FileOutputStream(outFile);
|
||||||
|
Utils.copy(in, out);
|
||||||
|
outFile.renameTo(new File(getCacheDir(), "b.dex"));
|
||||||
} catch (IOException e) {}
|
} catch (IOException e) {}
|
||||||
try {
|
try {
|
||||||
in.close();
|
in.close();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue