handle scalar in callee args and ret ty
This commit is contained in:
parent
573e899c2d
commit
23d1f51591
1 changed files with 231 additions and 54 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
use androscalpel::SmaliName;
|
use androscalpel::SmaliName;
|
||||||
use androscalpel::{IdMethod, Instruction, Method};
|
use androscalpel::{IdMethod, IdType, Instruction, Method};
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
@ -135,7 +135,7 @@ pub struct ReflectionCnstrNewInstData {
|
||||||
pub struct RegistersInfo {
|
pub struct RegistersInfo {
|
||||||
pub array_index: u8,
|
pub array_index: u8,
|
||||||
//pub array: u8,
|
//pub array: u8,
|
||||||
pub array_val: u8,
|
pub array_val: u8, // Reserver 2 reg here, for wide operation
|
||||||
pub array: u8,
|
pub array: u8,
|
||||||
//pub original_array_index_reg: Option<u16>,
|
//pub original_array_index_reg: Option<u16>,
|
||||||
//pub original_array_reg: Option<u16>,
|
//pub original_array_reg: Option<u16>,
|
||||||
|
|
@ -144,9 +144,9 @@ pub struct RegistersInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegistersInfo {
|
impl RegistersInfo {
|
||||||
const NB_U8_REG: u16 = 3;
|
const NB_U8_REG: u16 = 4; // array_val is a double register
|
||||||
fn get_nb_added_reg(&self) -> u16 {
|
fn get_nb_added_reg(&self) -> u16 {
|
||||||
3 + self.nb_arg_reg
|
4 + self.nb_arg_reg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,6 +191,48 @@ static CNSTR_GET_DEC_CLS: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static OBJ_TO_SCAL_BOOL: LazyLock<IdMethod> =
|
||||||
|
LazyLock::new(|| IdMethod::from_smali("Ljava/lang/Boolean;->booleanValue()Z").unwrap());
|
||||||
|
static OBJ_TO_SCAL_BYTE: LazyLock<IdMethod> =
|
||||||
|
LazyLock::new(|| IdMethod::from_smali("Ljava/lang/Byte;->byteValue()B").unwrap());
|
||||||
|
static OBJ_TO_SCAL_SHORT: LazyLock<IdMethod> =
|
||||||
|
LazyLock::new(|| IdMethod::from_smali("Ljava/lang/Short;->shortValue()S").unwrap());
|
||||||
|
static OBJ_TO_SCAL_CHAR: LazyLock<IdMethod> =
|
||||||
|
LazyLock::new(|| IdMethod::from_smali("Ljava/lang/Character;->charValue()C").unwrap());
|
||||||
|
static OBJ_TO_SCAL_INT: LazyLock<IdMethod> =
|
||||||
|
LazyLock::new(|| IdMethod::from_smali("Ljava/lang/Integer;->intValue()I").unwrap());
|
||||||
|
static OBJ_TO_SCAL_LONG: LazyLock<IdMethod> =
|
||||||
|
LazyLock::new(|| IdMethod::from_smali("Ljava/lang/Long;->longValue()J").unwrap());
|
||||||
|
static OBJ_TO_SCAL_FLOAT: LazyLock<IdMethod> =
|
||||||
|
LazyLock::new(|| IdMethod::from_smali("Ljava/lang/Float;->floatValue()F").unwrap());
|
||||||
|
static OBJ_TO_SCAL_DOUBLE: LazyLock<IdMethod> =
|
||||||
|
LazyLock::new(|| IdMethod::from_smali("Ljava/lang/Double;->doubleValue()D").unwrap());
|
||||||
|
static SCAL_TO_OBJ_BOOL: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
|
IdMethod::from_smali("Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;").unwrap()
|
||||||
|
});
|
||||||
|
static SCAL_TO_OBJ_BYTE: LazyLock<IdMethod> =
|
||||||
|
LazyLock::new(|| IdMethod::from_smali("Ljava/lang/Byte;->valueOf(B)Ljava/lang/Byte;").unwrap());
|
||||||
|
static SCAL_TO_OBJ_SHORT: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
|
IdMethod::from_smali("Ljava/lang/Short;->valueOf(S)Ljava/lang/Short;").unwrap()
|
||||||
|
});
|
||||||
|
static SCAL_TO_OBJ_CHAR: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
|
IdMethod::from_smali("Ljava/lang/Character;->valueOf(C)Ljava/lang/Character;").unwrap()
|
||||||
|
});
|
||||||
|
static SCAL_TO_OBJ_INT: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
|
IdMethod::from_smali("Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;").unwrap()
|
||||||
|
});
|
||||||
|
static SCAL_TO_OBJ_LONG: LazyLock<IdMethod> =
|
||||||
|
LazyLock::new(|| IdMethod::from_smali("Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;").unwrap());
|
||||||
|
static SCAL_TO_OBJ_FLOAT: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
|
IdMethod::from_smali("Ljava/lang/Float;->valueOf(F)Ljava/lang/Float;").unwrap()
|
||||||
|
});
|
||||||
|
static SCAL_TO_OBJ_DOUBLE: LazyLock<IdMethod> = LazyLock::new(|| {
|
||||||
|
IdMethod::from_smali("Ljava/lang/Double;->valueOf(D)Ljava/lang/Double;").unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
static OBJECT_TY: LazyLock<IdType> =
|
||||||
|
LazyLock::new(|| IdType::from_smali("Ljava/lang/Object;").unwrap());
|
||||||
|
|
||||||
/// Function passed to [`androscalpel::Apk::load_apk`] to label the instructions of interest.
|
/// Function passed to [`androscalpel::Apk::load_apk`] to label the instructions of interest.
|
||||||
pub fn labeling(_mth: &IdMethod, ins: &Instruction, addr: usize) -> Option<String> {
|
pub fn labeling(_mth: &IdMethod, ins: &Instruction, addr: usize) -> Option<String> {
|
||||||
match ins {
|
match ins {
|
||||||
|
|
@ -225,9 +267,9 @@ pub fn transform_method(meth: &mut Method, ref_data: &ReflectionData) -> Result<
|
||||||
let mut register_info = RegistersInfo {
|
let mut register_info = RegistersInfo {
|
||||||
array_index: code.registers_size as u8,
|
array_index: code.registers_size as u8,
|
||||||
array_val: (code.registers_size + 1) as u8,
|
array_val: (code.registers_size + 1) as u8,
|
||||||
array: (code.registers_size + 2) as u8,
|
array: (code.registers_size + 3) as u8,
|
||||||
//array: 0,
|
//array: 0,
|
||||||
first_arg: code.registers_size + 3,
|
first_arg: code.registers_size + 4,
|
||||||
nb_arg_reg: 0,
|
nb_arg_reg: 0,
|
||||||
};
|
};
|
||||||
let mut new_insns = vec![];
|
let mut new_insns = vec![];
|
||||||
|
|
@ -357,14 +399,23 @@ pub fn transform_method(meth: &mut Method, ref_data: &ReflectionData) -> Result<
|
||||||
/// Return the MoveResult{,Wide,Object} associated to the last instruction of the iterator.
|
/// Return the MoveResult{,Wide,Object} associated to the last instruction of the iterator.
|
||||||
/// TODO: return the list of pseudo instruction between the last instruction and the move result.
|
/// TODO: return the list of pseudo instruction between the last instruction and the move result.
|
||||||
fn get_move_result<'a>(
|
fn get_move_result<'a>(
|
||||||
mut iter: impl Iterator<Item = &'a Instruction>,
|
iter: impl Iterator<Item = &'a Instruction>,
|
||||||
) -> (Vec<Instruction>, Option<Instruction>) {
|
) -> (Vec<Instruction>, Option<Instruction>) {
|
||||||
if let Some(ins) = iter.next() {
|
let mut pseudo_insns = vec![];
|
||||||
|
for ins in iter {
|
||||||
|
/*
|
||||||
match ins {
|
match ins {
|
||||||
Instruction::MoveResult { .. }
|
Instruction::MoveResult { .. }
|
||||||
| Instruction::MoveResultWide { .. }
|
| Instruction::MoveResultWide { .. }
|
||||||
| Instruction::MoveResultObject { .. } => return (vec![], Some(ins.clone())),
|
| Instruction::MoveResultObject { .. } => return (vec![], Some(ins.clone())),
|
||||||
_ => (), // break,
|
_ => (), // break,
|
||||||
|
}*/
|
||||||
|
if ins.is_pseudo_ins() {
|
||||||
|
pseudo_insns.push(ins.clone());
|
||||||
|
} else if let Instruction::MoveResultObject { .. } = ins {
|
||||||
|
return (pseudo_insns, Some(ins.clone()));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(vec![], None)
|
(vec![], None)
|
||||||
|
|
@ -389,7 +440,13 @@ fn get_invoke_block(
|
||||||
// TODO
|
// TODO
|
||||||
bail!("Cannot transform invoke calls to a method using 16 bits register for its argument");
|
bail!("Cannot transform invoke calls to a method using 16 bits register for its argument");
|
||||||
}
|
}
|
||||||
let nb_args = ref_data.method.proto.get_parameters().len();
|
let nb_args: usize = ref_data
|
||||||
|
.method
|
||||||
|
.proto
|
||||||
|
.get_parameters()
|
||||||
|
.iter()
|
||||||
|
.map(|ty| if ty.is_double() || ty.is_long() { 2 } else { 1 })
|
||||||
|
.sum();
|
||||||
if reg_inf.nb_arg_reg < nb_args as u16 + 1 {
|
if reg_inf.nb_arg_reg < nb_args as u16 + 1 {
|
||||||
reg_inf.nb_arg_reg = nb_args as u16 + 1;
|
reg_inf.nb_arg_reg = nb_args as u16 + 1;
|
||||||
}
|
}
|
||||||
|
|
@ -421,31 +478,55 @@ fn get_invoke_block(
|
||||||
from: reg_inf.array_val as u16,
|
from: reg_inf.array_val as u16,
|
||||||
to: reg_inf.first_arg,
|
to: reg_inf.first_arg,
|
||||||
});
|
});
|
||||||
for (i, param) in ref_data.method.proto.get_parameters().iter().enumerate() {
|
insns.append(&mut get_args_from_obj_arr(
|
||||||
insns.push(Instruction::Const {
|
&ref_data.method.proto.get_parameters(),
|
||||||
reg: reg_inf.array_index,
|
arg_arr as u8, // TODO
|
||||||
lit: i as i32,
|
reg_inf.first_arg + 1,
|
||||||
});
|
reg_inf,
|
||||||
insns.push(Instruction::AGetObject {
|
));
|
||||||
dest: reg_inf.array_val,
|
|
||||||
arr: arg_arr as u8, // TODO
|
|
||||||
idx: reg_inf.array_index,
|
|
||||||
});
|
|
||||||
insns.push(Instruction::CheckCast {
|
|
||||||
reg: reg_inf.array_val,
|
|
||||||
lit: param.clone(),
|
|
||||||
});
|
|
||||||
insns.push(Instruction::MoveObject {
|
|
||||||
from: reg_inf.array_val as u16,
|
|
||||||
to: reg_inf.first_arg + 1 + i as u16,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
insns.push(Instruction::InvokeVirtual {
|
insns.push(Instruction::InvokeVirtual {
|
||||||
method: ref_data.method.clone(),
|
method: ref_data.method.clone(),
|
||||||
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 {
|
||||||
insns.push(move_result);
|
let ret_ty = ref_data.method.proto.get_return_type();
|
||||||
|
let res_reg = if let Instruction::MoveResultObject { to } = &move_result {
|
||||||
|
*to
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"`move_result` shloud always be a MoveResultObject, found {}",
|
||||||
|
move_result.__str__()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if ret_ty.is_class() || ret_ty.is_array() {
|
||||||
|
insns.push(move_result);
|
||||||
|
} else if ret_ty.is_double() || ret_ty.is_long() {
|
||||||
|
insns.push(Instruction::MoveResultWide {
|
||||||
|
to: reg_inf.array_val,
|
||||||
|
});
|
||||||
|
insns.push(Instruction::InvokeStatic {
|
||||||
|
method: get_scalar_to_obj_method(&ret_ty).unwrap(),
|
||||||
|
args: vec![reg_inf.array_val as u16],
|
||||||
|
});
|
||||||
|
insns.push(move_result);
|
||||||
|
insns.push(Instruction::CheckCast {
|
||||||
|
reg: res_reg,
|
||||||
|
lit: OBJECT_TY.clone(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
insns.push(Instruction::MoveResult {
|
||||||
|
to: reg_inf.array_val,
|
||||||
|
});
|
||||||
|
insns.push(Instruction::InvokeStatic {
|
||||||
|
method: get_scalar_to_obj_method(&ret_ty).unwrap(),
|
||||||
|
args: vec![reg_inf.array_val as u16],
|
||||||
|
});
|
||||||
|
insns.push(move_result);
|
||||||
|
insns.push(Instruction::CheckCast {
|
||||||
|
reg: res_reg,
|
||||||
|
lit: OBJECT_TY.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
insns.push(Instruction::Goto {
|
insns.push(Instruction::Goto {
|
||||||
label: end_label.to_string(),
|
label: end_label.to_string(),
|
||||||
|
|
@ -455,6 +536,121 @@ fn get_invoke_block(
|
||||||
Ok(insns)
|
Ok(insns)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the method that convert a object to its scalar conterpart (eg `java.lang.Integer` to `int` with
|
||||||
|
/// `Ljava/lang/Integer;->intValue()I`)
|
||||||
|
///
|
||||||
|
/// `scalar_ty` is the type of the scalar (eg `I`)
|
||||||
|
pub fn get_obj_to_scalar_method(scalar_ty: &IdType) -> Result<IdMethod> {
|
||||||
|
if scalar_ty == &IdType::boolean() {
|
||||||
|
Ok(OBJ_TO_SCAL_BOOL.clone())
|
||||||
|
} else if scalar_ty == &IdType::byte() {
|
||||||
|
Ok(OBJ_TO_SCAL_BYTE.clone())
|
||||||
|
} else if scalar_ty == &IdType::short() {
|
||||||
|
Ok(OBJ_TO_SCAL_SHORT.clone())
|
||||||
|
} else if scalar_ty == &IdType::char() {
|
||||||
|
Ok(OBJ_TO_SCAL_CHAR.clone())
|
||||||
|
} else if scalar_ty == &IdType::int() {
|
||||||
|
Ok(OBJ_TO_SCAL_INT.clone())
|
||||||
|
} else if scalar_ty == &IdType::long() {
|
||||||
|
Ok(OBJ_TO_SCAL_LONG.clone())
|
||||||
|
} else if scalar_ty == &IdType::float() {
|
||||||
|
Ok(OBJ_TO_SCAL_FLOAT.clone())
|
||||||
|
} else if scalar_ty == &IdType::double() {
|
||||||
|
Ok(OBJ_TO_SCAL_DOUBLE.clone())
|
||||||
|
} else {
|
||||||
|
bail!("{} is not a scalar", scalar_ty.__str__())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the method that convert a scalar to its object conterpart (eg `int` to `java.lang.Integer` with
|
||||||
|
/// `Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;`)
|
||||||
|
///
|
||||||
|
/// `scalar_ty` is the type of the scalar (eg `I`)
|
||||||
|
pub fn get_scalar_to_obj_method(scalar_ty: &IdType) -> Result<IdMethod> {
|
||||||
|
if scalar_ty == &IdType::boolean() {
|
||||||
|
Ok(SCAL_TO_OBJ_BOOL.clone())
|
||||||
|
} else if scalar_ty == &IdType::byte() {
|
||||||
|
Ok(SCAL_TO_OBJ_BYTE.clone())
|
||||||
|
} else if scalar_ty == &IdType::short() {
|
||||||
|
Ok(SCAL_TO_OBJ_SHORT.clone())
|
||||||
|
} else if scalar_ty == &IdType::char() {
|
||||||
|
Ok(SCAL_TO_OBJ_CHAR.clone())
|
||||||
|
} else if scalar_ty == &IdType::int() {
|
||||||
|
Ok(SCAL_TO_OBJ_INT.clone())
|
||||||
|
} else if scalar_ty == &IdType::long() {
|
||||||
|
Ok(SCAL_TO_OBJ_LONG.clone())
|
||||||
|
} else if scalar_ty == &IdType::float() {
|
||||||
|
Ok(SCAL_TO_OBJ_FLOAT.clone())
|
||||||
|
} else if scalar_ty == &IdType::double() {
|
||||||
|
Ok(SCAL_TO_OBJ_DOUBLE.clone())
|
||||||
|
} else {
|
||||||
|
bail!("{} is not a scalar", scalar_ty.__str__())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate bytecode that put the arguments of types `params` from an [java.lang.Object to
|
||||||
|
/// types consecutive registers starting at `first_arg_reg`.
|
||||||
|
/// `first_arg_reg` sould be `reg_inf.first_arg` or `reg_inf.first_arg+1` depending on if this
|
||||||
|
/// is for a static or virtual call.
|
||||||
|
pub fn get_args_from_obj_arr(
|
||||||
|
params: &[IdType],
|
||||||
|
array_reg: u8,
|
||||||
|
first_arg_reg: u16,
|
||||||
|
reg_inf: &mut RegistersInfo,
|
||||||
|
) -> Vec<Instruction> {
|
||||||
|
let mut insns = vec![];
|
||||||
|
let mut reg_count = 0;
|
||||||
|
for (i, param) in params.iter().enumerate() {
|
||||||
|
insns.push(Instruction::Const {
|
||||||
|
reg: reg_inf.array_index,
|
||||||
|
lit: i as i32,
|
||||||
|
});
|
||||||
|
insns.push(Instruction::AGetObject {
|
||||||
|
dest: reg_inf.array_val,
|
||||||
|
arr: array_reg,
|
||||||
|
idx: reg_inf.array_index,
|
||||||
|
});
|
||||||
|
if param.is_class() || param.is_array() {
|
||||||
|
insns.push(Instruction::CheckCast {
|
||||||
|
reg: reg_inf.array_val,
|
||||||
|
lit: param.clone(),
|
||||||
|
});
|
||||||
|
insns.push(Instruction::MoveObject {
|
||||||
|
from: reg_inf.array_val as u16,
|
||||||
|
to: first_arg_reg + reg_count,
|
||||||
|
});
|
||||||
|
reg_count += 1;
|
||||||
|
} else if param.is_double() || param.is_long() {
|
||||||
|
insns.push(Instruction::InvokeVirtual {
|
||||||
|
method: get_obj_to_scalar_method(param).unwrap(),
|
||||||
|
args: vec![reg_inf.array_val as u16],
|
||||||
|
});
|
||||||
|
insns.push(Instruction::MoveResultWide {
|
||||||
|
to: reg_inf.array_val,
|
||||||
|
});
|
||||||
|
insns.push(Instruction::MoveWide {
|
||||||
|
from: reg_inf.array_val as u16,
|
||||||
|
to: first_arg_reg + reg_count,
|
||||||
|
});
|
||||||
|
reg_count += 2;
|
||||||
|
} else {
|
||||||
|
insns.push(Instruction::InvokeVirtual {
|
||||||
|
method: get_obj_to_scalar_method(param).unwrap(),
|
||||||
|
args: vec![reg_inf.array_val as u16],
|
||||||
|
});
|
||||||
|
insns.push(Instruction::MoveResult {
|
||||||
|
to: reg_inf.array_val,
|
||||||
|
});
|
||||||
|
insns.push(Instruction::Move {
|
||||||
|
from: reg_inf.array_val as u16,
|
||||||
|
to: first_arg_reg + reg_count,
|
||||||
|
});
|
||||||
|
reg_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insns
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate bytecode that test if a `java.lang.reflect.Method` is equal to an [`IdMethod`]
|
/// Generate bytecode that test if a `java.lang.reflect.Method` is equal to an [`IdMethod`]
|
||||||
///
|
///
|
||||||
/// - `method_obj_reg`: the register containing the `java.lang.reflect.Method`
|
/// - `method_obj_reg`: the register containing the `java.lang.reflect.Method`
|
||||||
|
|
@ -614,31 +810,12 @@ fn get_cnstr_new_inst_block(
|
||||||
abort_label.clone(),
|
abort_label.clone(),
|
||||||
reg_inf,
|
reg_inf,
|
||||||
);
|
);
|
||||||
for (i, param) in ref_data
|
insns.append(&mut get_args_from_obj_arr(
|
||||||
.constructor
|
&ref_data.constructor.proto.get_parameters(),
|
||||||
.proto
|
arg_arr as u8, // TODO
|
||||||
.get_parameters()
|
reg_inf.first_arg + 1,
|
||||||
.iter()
|
reg_inf,
|
||||||
.enumerate()
|
));
|
||||||
{
|
|
||||||
insns.push(Instruction::Const {
|
|
||||||
reg: reg_inf.array_index,
|
|
||||||
lit: i as i32,
|
|
||||||
});
|
|
||||||
insns.push(Instruction::AGetObject {
|
|
||||||
dest: reg_inf.array_val,
|
|
||||||
arr: arg_arr as u8, // TODO
|
|
||||||
idx: reg_inf.array_index,
|
|
||||||
});
|
|
||||||
insns.push(Instruction::CheckCast {
|
|
||||||
reg: reg_inf.array_val,
|
|
||||||
lit: param.clone(),
|
|
||||||
});
|
|
||||||
insns.push(Instruction::MoveObject {
|
|
||||||
from: reg_inf.array_val as u16,
|
|
||||||
to: reg_inf.first_arg + i as u16 + 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
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.constructor.class_.clone(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue