wip
This commit is contained in:
parent
a374769389
commit
edd15fce67
4 changed files with 54 additions and 28 deletions
|
|
@ -45,7 +45,7 @@ def on_message(message, data, data_storage: dict, file_storage: Path):
|
||||||
handle_cnstr_new_inst_data(message["payload"]["data"], data_storage)
|
handle_cnstr_new_inst_data(message["payload"]["data"], data_storage)
|
||||||
elif message["type"] == "send" and message["payload"]["type"] == "load-dex":
|
elif message["type"] == "send" and message["payload"]["type"] == "load-dex":
|
||||||
handle_load_dex(message["payload"]["data"], data_storage, file_storage)
|
handle_load_dex(message["payload"]["data"], data_storage, file_storage)
|
||||||
elif message["type"] == "send" and message["payload"]["type"] == "apk-cl":
|
elif message["type"] == "send" and message["payload"]["type"] == "classloader":
|
||||||
handle_classloader_data(message["payload"]["data"], data_storage)
|
handle_classloader_data(message["payload"]["data"], data_storage)
|
||||||
else:
|
else:
|
||||||
print("[-] message:", message)
|
print("[-] message:", message)
|
||||||
|
|
@ -59,13 +59,20 @@ def print_stack(stack, prefix: str):
|
||||||
print(f" {prefix}{frame['method']}:{frame['bytecode_index']}{native}")
|
print(f" {prefix}{frame['method']}:{frame['bytecode_index']}{native}")
|
||||||
|
|
||||||
|
|
||||||
|
def cl_id_to_string(classloader: int) -> str:
|
||||||
|
if classloader < 0:
|
||||||
|
classloader += 2 << (HASH_NB_BYTES * 8 - 1)
|
||||||
|
return classloader.to_bytes(HASH_NB_BYTES).hex()
|
||||||
|
|
||||||
|
|
||||||
def handle_classloader_data(data: dict, data_storage: dict):
|
def handle_classloader_data(data: dict, data_storage: dict):
|
||||||
|
data["id"] = cl_id_to_string(data["id"])
|
||||||
data_storage["initial_classloaders"].append(data)
|
data_storage["initial_classloaders"].append(data)
|
||||||
|
|
||||||
|
|
||||||
def handle_invoke_data(data, data_storage: dict):
|
def handle_invoke_data(data, data_storage: dict):
|
||||||
method = data["method"]
|
method = data["method"]
|
||||||
method_cl_id = data["method_cl_id"]
|
method_cl_id = cl_id_to_string(data["method_cl_id"])
|
||||||
# TODO: good idea?
|
# TODO: good idea?
|
||||||
if method in [
|
if method in [
|
||||||
"Landroid/view/View;->getTranslationZ()F",
|
"Landroid/view/View;->getTranslationZ()F",
|
||||||
|
|
@ -75,7 +82,7 @@ def handle_invoke_data(data, data_storage: dict):
|
||||||
if len(data["stack"]) == 0:
|
if len(data["stack"]) == 0:
|
||||||
return
|
return
|
||||||
caller_method = data["stack"][0]["method"]
|
caller_method = data["stack"][0]["method"]
|
||||||
caller_cl_id = data["stack"][0]["cl_id"]
|
caller_cl_id = cl_id_to_string(data["stack"][0]["cl_id"])
|
||||||
addr = data["stack"][0]["bytecode_index"]
|
addr = data["stack"][0]["bytecode_index"]
|
||||||
is_static = data["is_static"]
|
is_static = data["is_static"]
|
||||||
if is_static:
|
if is_static:
|
||||||
|
|
@ -106,7 +113,7 @@ def handle_invoke_data(data, data_storage: dict):
|
||||||
|
|
||||||
def handle_class_new_inst_data(data, data_storage: dict):
|
def handle_class_new_inst_data(data, data_storage: dict):
|
||||||
constructor = data["constructor"]
|
constructor = data["constructor"]
|
||||||
constructor_cl_id = data["constructor_cl_id"]
|
constructor_cl_id = cl_id_to_string(data["constructor_cl_id"])
|
||||||
if len(data["stack"]) == 0:
|
if len(data["stack"]) == 0:
|
||||||
return
|
return
|
||||||
if (
|
if (
|
||||||
|
|
@ -119,7 +126,7 @@ def handle_class_new_inst_data(data, data_storage: dict):
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
caller_method = frame["method"]
|
caller_method = frame["method"]
|
||||||
caller_cl_id = frame["cl_id"]
|
caller_cl_id = cl_id_to_string(frame["cl_id"])
|
||||||
addr = frame["bytecode_index"]
|
addr = frame["bytecode_index"]
|
||||||
print("[+] Class.NewInstance:")
|
print("[+] Class.NewInstance:")
|
||||||
print(f" called: [{constructor_cl_id}]{constructor}")
|
print(f" called: [{constructor_cl_id}]{constructor}")
|
||||||
|
|
@ -144,13 +151,13 @@ def handle_class_new_inst_data(data, data_storage: dict):
|
||||||
|
|
||||||
def handle_cnstr_new_inst_data(data, data_storage: dict):
|
def handle_cnstr_new_inst_data(data, data_storage: dict):
|
||||||
constructor = data["constructor"]
|
constructor = data["constructor"]
|
||||||
constructor_cl_id = data["constructor_cl_id"]
|
constructor_cl_id = cl_id_to_string(data["constructor_cl_id"])
|
||||||
if not constructor.startswith("Lcom/example/theseus"):
|
if not constructor.startswith("Lcom/example/theseus"):
|
||||||
return
|
return
|
||||||
if len(data["stack"]) == 0:
|
if len(data["stack"]) == 0:
|
||||||
return
|
return
|
||||||
caller_method = data["stack"][0]["method"]
|
caller_method = data["stack"][0]["method"]
|
||||||
caller_cl_id = data["stack"][0]["cl_id"]
|
caller_cl_id = cl_id_to_string(data["stack"][0]["cl_id"])
|
||||||
addr = data["stack"][0]["bytecode_index"]
|
addr = data["stack"][0]["bytecode_index"]
|
||||||
print("[+] Constructor.newInstance:")
|
print("[+] Constructor.newInstance:")
|
||||||
print(f" called: [{constructor_cl_id}]{constructor}")
|
print(f" called: [{constructor_cl_id}]{constructor}")
|
||||||
|
|
@ -176,10 +183,7 @@ def handle_cnstr_new_inst_data(data, data_storage: dict):
|
||||||
def handle_load_dex(data, data_storage: dict, file_storage: Path):
|
def handle_load_dex(data, data_storage: dict, file_storage: Path):
|
||||||
dex = data["dex"]
|
dex = data["dex"]
|
||||||
classloader_class = data["classloader_class"]
|
classloader_class = data["classloader_class"]
|
||||||
classloader = data["classloader"]
|
classloader = cl_id_to_string(data["classloader"])
|
||||||
if classloader < 0:
|
|
||||||
classloader += 2 << (HASH_NB_BYTES * 8 - 1)
|
|
||||||
classloader = classloader.to_bytes(HASH_NB_BYTES).hex()
|
|
||||||
short_class = classloader_class.split("/")[-1].removesuffix(";")
|
short_class = classloader_class.split("/")[-1].removesuffix(";")
|
||||||
files = []
|
files = []
|
||||||
print("[+] DEX file loaded:")
|
print("[+] DEX file loaded:")
|
||||||
|
|
@ -360,7 +364,7 @@ def collect_runtime(apk: Path, device_name: str, file_storage: Path, output: Tex
|
||||||
for load_data in data_storage["dyn_code_load"]:
|
for load_data in data_storage["dyn_code_load"]:
|
||||||
if load_data["classloader"] in cls:
|
if load_data["classloader"] in cls:
|
||||||
del cls[load_data["classloader"]]
|
del cls[load_data["classloader"]]
|
||||||
for id_ in cls.keys():
|
for id_ in list(cls.keys()):
|
||||||
if (
|
if (
|
||||||
'dalvik.system.PathClassLoader[DexPathList[[directory "."],'
|
'dalvik.system.PathClassLoader[DexPathList[[directory "."],'
|
||||||
in cls[id_]["str"]
|
in cls[id_]["str"]
|
||||||
|
|
|
||||||
|
|
@ -627,7 +627,7 @@ fn get_invoke_block(
|
||||||
});
|
});
|
||||||
insns.push(Instruction::CheckCast {
|
insns.push(Instruction::CheckCast {
|
||||||
reg: reg_inf.array_val,
|
reg: reg_inf.array_val,
|
||||||
lit: ref_data.method.class_.clone(),
|
lit: ref_data.get_static_callee().class_,
|
||||||
});
|
});
|
||||||
insns.push(Instruction::MoveObject {
|
insns.push(Instruction::MoveObject {
|
||||||
from: reg_inf.array_val as u16,
|
from: reg_inf.array_val as u16,
|
||||||
|
|
@ -635,24 +635,24 @@ fn get_invoke_block(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
insns.append(&mut get_args_from_obj_arr(
|
insns.append(&mut get_args_from_obj_arr(
|
||||||
&ref_data.method.proto.get_parameters(),
|
&ref_data.get_static_callee().proto.get_parameters(), // TODO: what if renambed args?
|
||||||
arg_arr,
|
arg_arr,
|
||||||
reg_inf.first_arg + if ref_data.is_static { 0 } else { 1 },
|
reg_inf.first_arg + if ref_data.is_static { 0 } else { 1 },
|
||||||
reg_inf,
|
reg_inf,
|
||||||
));
|
));
|
||||||
if ref_data.is_static {
|
if ref_data.is_static {
|
||||||
insns.push(Instruction::InvokeStatic {
|
insns.push(Instruction::InvokeStatic {
|
||||||
method: ref_data.method.clone(),
|
method: ref_data.get_static_callee(),
|
||||||
args: (reg_inf.first_arg..reg_inf.first_arg + nb_args as u16).collect(),
|
args: (reg_inf.first_arg..reg_inf.first_arg + nb_args as u16).collect(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
insns.push(Instruction::InvokeVirtual {
|
insns.push(Instruction::InvokeVirtual {
|
||||||
method: ref_data.method.clone(),
|
method: ref_data.get_static_callee(),
|
||||||
args: (reg_inf.first_arg..reg_inf.first_arg + 1 + nb_args as u16).collect(),
|
args: (reg_inf.first_arg..reg_inf.first_arg + 1 + nb_args as u16).collect(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if let Some(move_result) = move_result {
|
if let Some(move_result) = move_result {
|
||||||
let ret_ty = ref_data.method.proto.get_return_type();
|
let ret_ty = ref_data.get_static_callee().proto.get_return_type();
|
||||||
let res_reg = if let Instruction::MoveResultObject { to } = &move_result {
|
let res_reg = if let Instruction::MoveResultObject { to } = &move_result {
|
||||||
*to
|
*to
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -816,14 +816,14 @@ fn get_cnstr_new_inst_block(
|
||||||
|
|
||||||
let mut insns = test_cnstr(
|
let mut insns = test_cnstr(
|
||||||
cnst_reg,
|
cnst_reg,
|
||||||
ref_data.constructor.clone(),
|
ref_data.constructor.clone(), // TODO: what if args are renammed?
|
||||||
abort_label.clone(),
|
abort_label.clone(),
|
||||||
reg_inf,
|
reg_inf,
|
||||||
tester_methods_class,
|
tester_methods_class,
|
||||||
tester_methods,
|
tester_methods,
|
||||||
)?;
|
)?;
|
||||||
insns.append(&mut get_args_from_obj_arr(
|
insns.append(&mut get_args_from_obj_arr(
|
||||||
&ref_data.constructor.proto.get_parameters(),
|
&ref_data.constructor.proto.get_parameters(), // TODO: what if args are renammed?
|
||||||
arg_arr,
|
arg_arr,
|
||||||
reg_inf.first_arg + 1,
|
reg_inf.first_arg + 1,
|
||||||
reg_inf,
|
reg_inf,
|
||||||
|
|
@ -831,12 +831,12 @@ fn get_cnstr_new_inst_block(
|
||||||
if reg_inf.first_arg < u8::MAX as u16 {
|
if reg_inf.first_arg < u8::MAX as u16 {
|
||||||
insns.push(Instruction::NewInstance {
|
insns.push(Instruction::NewInstance {
|
||||||
reg: reg_inf.first_arg as u8,
|
reg: reg_inf.first_arg as u8,
|
||||||
lit: ref_data.constructor.class_.clone(),
|
lit: ref_data.get_static_constructor().class_,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
insns.push(Instruction::NewInstance {
|
insns.push(Instruction::NewInstance {
|
||||||
reg: reg_inf.array_val,
|
reg: reg_inf.array_val,
|
||||||
lit: ref_data.constructor.class_.clone(),
|
lit: ref_data.get_static_constructor().class_,
|
||||||
});
|
});
|
||||||
insns.push(Instruction::MoveObject {
|
insns.push(Instruction::MoveObject {
|
||||||
from: reg_inf.array_val as u16,
|
from: reg_inf.array_val as u16,
|
||||||
|
|
@ -844,7 +844,7 @@ fn get_cnstr_new_inst_block(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
insns.push(Instruction::InvokeDirect {
|
insns.push(Instruction::InvokeDirect {
|
||||||
method: ref_data.constructor.clone(),
|
method: ref_data.get_static_constructor(),
|
||||||
args: (reg_inf.first_arg..reg_inf.first_arg + nb_args as u16 + 1).collect(),
|
args: (reg_inf.first_arg..reg_inf.first_arg + nb_args as u16 + 1).collect(),
|
||||||
});
|
});
|
||||||
if let Some(Instruction::MoveResultObject { to }) = move_result {
|
if let Some(Instruction::MoveResultObject { to }) = move_result {
|
||||||
|
|
@ -916,10 +916,6 @@ fn get_class_new_inst_block(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if class_reg > u8::MAX as u16 {
|
|
||||||
// TODO
|
|
||||||
bail!("Cannot transform instantiation calls to a class stored in 16 bits register");
|
|
||||||
}
|
|
||||||
let class_reg = class_reg as u8;
|
let class_reg = class_reg as u8;
|
||||||
|
|
||||||
let abort_label = format!(
|
let abort_label = format!(
|
||||||
|
|
@ -970,10 +966,10 @@ fn get_class_new_inst_block(
|
||||||
//},
|
//},
|
||||||
Instruction::NewInstance {
|
Instruction::NewInstance {
|
||||||
reg: obj_reg,
|
reg: obj_reg,
|
||||||
lit: ref_data.constructor.class_.clone(),
|
lit: ref_data.get_static_constructor().class_.clone(),
|
||||||
},
|
},
|
||||||
Instruction::InvokeDirect {
|
Instruction::InvokeDirect {
|
||||||
method: ref_data.constructor.clone(),
|
method: ref_data.get_static_constructor().clone(),
|
||||||
args: vec![obj_reg as u16],
|
args: vec![obj_reg as u16],
|
||||||
},
|
},
|
||||||
Instruction::Goto {
|
Instruction::Goto {
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,14 @@ pub struct ReflectionInvokeData {
|
||||||
// TODO: type of invoke?
|
// TODO: type of invoke?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ReflectionInvokeData {
|
||||||
|
pub fn get_static_callee(&self) -> IdMethod {
|
||||||
|
self.renamed_method
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| self.method.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Structure storing the runtime information of a reflection instanciation using
|
/// Structure storing the runtime information of a reflection instanciation using
|
||||||
/// `java.lang.Class.newInstance()`.
|
/// `java.lang.Class.newInstance()`.
|
||||||
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
|
|
@ -132,6 +140,14 @@ pub struct ReflectionClassNewInstData {
|
||||||
pub addr: usize,
|
pub addr: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ReflectionClassNewInstData {
|
||||||
|
pub fn get_static_constructor(&self) -> IdMethod {
|
||||||
|
self.renamed_constructor
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| self.constructor.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Structure storing the runtime information of a reflection instanciation using
|
/// Structure storing the runtime information of a reflection instanciation using
|
||||||
/// `java.lang.reflect.Constructor.newInstance()`.
|
/// `java.lang.reflect.Constructor.newInstance()`.
|
||||||
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
|
|
@ -152,6 +168,14 @@ pub struct ReflectionCnstrNewInstData {
|
||||||
pub addr: usize,
|
pub addr: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ReflectionCnstrNewInstData {
|
||||||
|
pub fn get_static_constructor(&self) -> IdMethod {
|
||||||
|
self.renamed_constructor
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| self.constructor.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Structure storing the runtime information of a dynamic code loading.
|
/// Structure storing the runtime information of a dynamic code loading.
|
||||||
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub struct DynamicCodeLoadingData {
|
pub struct DynamicCodeLoadingData {
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,8 @@ def patch_apk(
|
||||||
str(zipalign.absolute()),
|
str(zipalign.absolute()),
|
||||||
"-a",
|
"-a",
|
||||||
str(apksigner.absolute()),
|
str(apksigner.absolute()),
|
||||||
|
"--code-loading-patch-strategy",
|
||||||
|
"model-class-loaders",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue