fix string order
This commit is contained in:
parent
0b8dce9266
commit
3a7208f1b5
4 changed files with 227 additions and 56 deletions
|
|
@ -227,6 +227,28 @@ impl IdType {
|
|||
Self(ty)
|
||||
}
|
||||
|
||||
/// Try to parse a smali representation of type into a IdType.
|
||||
///
|
||||
/// In practice, it's juste wrapper arround `Self::new()` for consistancy with other
|
||||
/// id types.
|
||||
///
|
||||
/// ```
|
||||
/// use androscalpel::IdType
|
||||
///
|
||||
/// let id_type = IdType::from_smali(
|
||||
/// "Landroidx/core/util/Predicate;"
|
||||
/// ).unwrap();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// id_type,
|
||||
/// IdType::class("androidx/core/util/Predicate")
|
||||
/// );
|
||||
/// ```
|
||||
#[staticmethod]
|
||||
pub fn from_smali(smali_repr: &str) -> Result<Self> {
|
||||
Self::new(smali_repr.into())
|
||||
}
|
||||
|
||||
/// Return a list of types from a string of concatenated types (like the ones between
|
||||
/// parentheses in the repr of a prototype)
|
||||
///
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ fn test_generated_apk_equivalence() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_order() {
|
||||
fn test_string_order_in_dex() {
|
||||
use std::collections::HashSet;
|
||||
let dex = DexFileReader::new(get_hello_world_dex()).unwrap();
|
||||
let new_dex = DexFileReader::new(get_hello_world_recompilled()).unwrap();
|
||||
|
|
@ -67,7 +67,28 @@ fn test_string_order() {
|
|||
println!("{}", string.__str__());
|
||||
}
|
||||
}
|
||||
assert_eq!(dex.get_string_ids().len(), new_dex.get_string_ids().len());
|
||||
assert_eq!(
|
||||
dex.get_string_ids().len(),
|
||||
new_dex.get_string_ids().len(),
|
||||
"Not the same number of strings"
|
||||
);
|
||||
/* this is hell to debug
|
||||
let strings: Vec<_> = (0..dex.get_string_ids().len())
|
||||
.map(|idx| DexString(dex.get_string(idx as u32).unwrap()))
|
||||
.collect();
|
||||
let new_strings: Vec<_> = (0..new_dex.get_string_ids().len())
|
||||
.map(|idx| DexString(new_dex.get_string(idx as u32).unwrap()))
|
||||
.collect();
|
||||
assert_eq!(strings, new_strings);
|
||||
*/
|
||||
for idx in 0..dex.get_string_ids().len() {
|
||||
let string = DexString(dex.get_string(idx as u32).unwrap());
|
||||
let new_string = DexString(new_dex.get_string(idx as u32).unwrap());
|
||||
assert_eq!(
|
||||
string, new_string,
|
||||
"Strings {idx} do not match, left original, right recompiled"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -141,3 +162,58 @@ fn test_parse_field_smali() {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_fields() {
|
||||
let f1 = IdField::from_smali(
|
||||
"Landroidx/lifecycle/WithLifecycleStateKt$suspendWithStateAtLeastUnchecked$2$observer$1;\
|
||||
->$co:Lkotlinx/coroutines/CancellableContinuation;",
|
||||
)
|
||||
.unwrap();
|
||||
let f2 = IdField::from_smali(
|
||||
"Lcom/google/android/material/search/SearchBarAnimationHelper$$ExternalSyntheticLambda4;\
|
||||
->f$0:Landroid/animation/Animator;",
|
||||
)
|
||||
.unwrap();list1
|
||||
let mut list1 = vec![f2.clone(), f1.clone()];
|
||||
let mut list2 = vec![f1.clone(), f2.clone()];
|
||||
list1.sort();
|
||||
list2.sort();
|
||||
assert_eq!(list1, list2);
|
||||
assert_eq!(list1, vec![f1.clone(), f2.clone()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_types() {
|
||||
let t1 = IdType::from_smali(
|
||||
"Landroidx/lifecycle/WithLifecycleStateKt$suspendWithStateAtLeastUnchecked$2$observer$1;",
|
||||
)
|
||||
.unwrap();
|
||||
let t2 = IdType::from_smali(
|
||||
"Lcom/google/android/material/search/SearchBarAnimationHelper$$ExternalSyntheticLambda4;",
|
||||
)
|
||||
.unwrap();
|
||||
let mut list1 = vec![t2.clone(), t1.clone()];
|
||||
let mut list2 = vec![t1.clone(), t2.clone()];
|
||||
list1.sort();
|
||||
list2.sort();
|
||||
assert_eq!(list1, list2);
|
||||
assert_eq!(list1, vec![t1.clone(), t2.clone()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_strings() {
|
||||
let s1: DexString =
|
||||
"Landroidx/lifecycle/WithLifecycleStateKt$suspendWithStateAtLeastUnchecked$2$observer$1;"
|
||||
.into();
|
||||
let s2: DexString =
|
||||
"Lcom/google/android/material/search/SearchBarAnimationHelper$$ExternalSyntheticLambda4;"
|
||||
.into();
|
||||
|
||||
let mut list1 = vec![s2.clone(), s1.clone()];
|
||||
let mut list2 = vec![s1.clone(), s2.clone()];
|
||||
list1.sort();
|
||||
list2.sort();
|
||||
assert_eq!(list1, list2);
|
||||
assert_eq!(list1, vec![s1.clone(), s2.clone()]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ const VALUE_TRAYLING_BYTE_PREFIX: u8 = 0b1000_0000;
|
|||
|
||||
impl Ord for StringDataItem {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.data
|
||||
.cmp(&other.data)
|
||||
self.get_aosp_utf16()
|
||||
.unwrap()
|
||||
.cmp(&other.get_aosp_utf16().unwrap())
|
||||
.then(self.utf16_size.cmp(&other.utf16_size))
|
||||
}
|
||||
}
|
||||
|
|
@ -122,6 +123,57 @@ impl From<&str> for StringDataItem {
|
|||
}
|
||||
|
||||
impl StringDataItem {
|
||||
/// Return the utf-16 string used by google in the aosp for comparaison & other.
|
||||
fn get_aosp_utf16(&self) -> Result<Vec<u32>> {
|
||||
let mut utf16_string = vec![];
|
||||
let mut i = 0;
|
||||
while i < self.data.len() {
|
||||
let one = self.data[i] as u32;
|
||||
i += 1;
|
||||
if one & 0x80 == 0 {
|
||||
utf16_string.push(one);
|
||||
continue;
|
||||
}
|
||||
|
||||
if i >= self.data.len() {
|
||||
return Err(Error::InvalidStringEncoding(
|
||||
"String contains invalid caracters".into(),
|
||||
));
|
||||
}
|
||||
let two = self.data[i] as u32;
|
||||
i += 1;
|
||||
if one & 0x20 == 0 {
|
||||
utf16_string.push((one & 0x1f) << 6 | (two & 0x3f));
|
||||
continue;
|
||||
}
|
||||
|
||||
if i >= self.data.len() {
|
||||
return Err(Error::InvalidStringEncoding(
|
||||
"String contains invalid caracters".into(),
|
||||
));
|
||||
}
|
||||
let three = self.data[i] as u32;
|
||||
i += 1;
|
||||
if one & 0x10 == 0 {
|
||||
utf16_string.push((one & 0x0f) << 12 | (two & 0x3f) << 6 | (three & 0x3f));
|
||||
continue;
|
||||
}
|
||||
|
||||
if i >= self.data.len() {
|
||||
return Err(Error::InvalidStringEncoding(
|
||||
"String contains invalid caracters".into(),
|
||||
));
|
||||
}
|
||||
let four = self.data[i] as u32;
|
||||
i += 1;
|
||||
let code_point =
|
||||
(one & 0x0f) << 18 | (two & 0x3f) << 12 | (three & 0x3f) << 6 | (four & 0x3f);
|
||||
let mut pair = ((code_point >> 10) + 0xd7c0) & 0xffff;
|
||||
pair |= ((code_point & 0x03ff) + 0xdc00) << 16;
|
||||
utf16_string.push(pair);
|
||||
}
|
||||
Ok(utf16_string)
|
||||
}
|
||||
fn get_string(&self) -> Result<String> {
|
||||
let mut string = String::new();
|
||||
let mut i = 0;
|
||||
|
|
@ -481,4 +533,25 @@ mod test {
|
|||
assert_eq!(encoded, expected.as_str().into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Apparently I don't know how to code an order relation so here it is...
|
||||
#[test]
|
||||
fn test_ord_relation() {
|
||||
let s1: StringDataItem = "Landroidx/lifecycle/WithLifecycleStateKt$suspendWithStateAtLeastUnchecked$2$observer$1;".into();
|
||||
let s2: StringDataItem = "Lcom/google/android/material/search/SearchBarAnimationHelper$$ExternalSyntheticLambda4;".into();
|
||||
let s1_utf16 = s1.get_aosp_utf16().unwrap();
|
||||
let s2_utf16 = s2.get_aosp_utf16().unwrap();
|
||||
assert_eq!(s1_utf16 < s2_utf16, true);
|
||||
assert_eq!(s1_utf16 == s2_utf16, false);
|
||||
assert_eq!(s1_utf16 > s2_utf16, false);
|
||||
assert_eq!(s2_utf16 < s1_utf16, false);
|
||||
assert_eq!(s2_utf16 == s1_utf16, false);
|
||||
assert_eq!(s2_utf16 > s1_utf16, true);
|
||||
assert_eq!(s1 < s2, true);
|
||||
assert_eq!(s1 == s2, false);
|
||||
assert_eq!(s1 > s2, false);
|
||||
assert_eq!(s2 < s1, false);
|
||||
assert_eq!(s2 == s1, false);
|
||||
assert_eq!(s2 > s1, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
104
test.py
104
test.py
|
|
@ -22,43 +22,43 @@ with z.ZipFile(APK_NAME) as zipf:
|
|||
apk = Apk()
|
||||
apk.add_dex_file(dex)
|
||||
|
||||
# clazz_id = IdType("Lcom/example/testapplication/ui/home/HomeViewModel;")
|
||||
# proto_id = IdMethodType(IdType("Ljava/lang/String;"), [])
|
||||
# method_id = IdMethod("text_gen", proto_id, clazz_id)
|
||||
#
|
||||
# clazz = apk.classes[clazz_id]
|
||||
# method = clazz.virtual_methods[method_id]
|
||||
# code = method.code
|
||||
#
|
||||
clazz_id = IdType("Lcom/example/testapplication/ui/home/HomeViewModel;")
|
||||
proto_id = IdMethodType(IdType("Ljava/lang/String;"), [])
|
||||
method_id = IdMethod("text_gen", proto_id, clazz_id)
|
||||
|
||||
clazz = apk.classes[clazz_id]
|
||||
method = clazz.virtual_methods[method_id]
|
||||
code = method.code
|
||||
|
||||
logging.getLogger().setLevel(logging.WARNING)
|
||||
#
|
||||
# print(f"[+] Code of {method_id} ")
|
||||
# for i in code.insns:
|
||||
# print(f" {i}")
|
||||
# print("[+] Modify code")
|
||||
#
|
||||
# new_insns = []
|
||||
# for i in code.insns:
|
||||
# if isinstance(i, ins.ConstString):
|
||||
# if i.lit == "Hello":
|
||||
# i = ins.ConstString(i.reg, DexString("Degemer Mat"))
|
||||
# elif i.lit == "Bye":
|
||||
# i = ins.ConstString(i.reg, DexString("Kenavo"))
|
||||
# new_insns.append(i)
|
||||
#
|
||||
# # This need improving!
|
||||
# code = Code(code.registers_size, code.ins_size, code.outs_size, new_insns)
|
||||
# apk.set_method_code(method_id, code)
|
||||
# # apk.set_method_code(method.descriptor, code)
|
||||
#
|
||||
#
|
||||
# clazz = apk.classes[clazz_id]
|
||||
# method = clazz.virtual_methods[method_id]
|
||||
# code = method.code
|
||||
# print(f"[+] New code of {method_id} ")
|
||||
# for i in code.insns:
|
||||
# print(f" {i}")
|
||||
#
|
||||
|
||||
print(f"[+] Code of {method_id} ")
|
||||
for i in code.insns:
|
||||
print(f" {i}")
|
||||
print("[+] Modify code")
|
||||
|
||||
new_insns = []
|
||||
for i in code.insns:
|
||||
if isinstance(i, ins.ConstString):
|
||||
if i.lit == "Hello":
|
||||
i = ins.ConstString(i.reg, DexString("Degemer Mat"))
|
||||
elif i.lit == "Bye":
|
||||
i = ins.ConstString(i.reg, DexString("Kenavo"))
|
||||
new_insns.append(i)
|
||||
|
||||
# This need improving!
|
||||
code = Code(code.registers_size, code.ins_size, code.outs_size, new_insns)
|
||||
apk.set_method_code(method_id, code)
|
||||
# apk.set_method_code(method.descriptor, code)
|
||||
|
||||
|
||||
clazz = apk.classes[clazz_id]
|
||||
method = clazz.virtual_methods[method_id]
|
||||
code = method.code
|
||||
print(f"[+] New code of {method_id} ")
|
||||
for i in code.insns:
|
||||
print(f" {i}")
|
||||
|
||||
# # Strip class for debugging
|
||||
# classes = list(
|
||||
# filter(
|
||||
|
|
@ -80,22 +80,22 @@ print("[+] Recompile")
|
|||
|
||||
dex_raw = apk.gen_raw_dex()
|
||||
|
||||
new_apk = Apk()
|
||||
for dex in dex_raw:
|
||||
new_apk.add_dex_file(dex)
|
||||
# new_apk = Apk()
|
||||
# for dex in dex_raw:
|
||||
# new_apk.add_dex_file(dex)
|
||||
|
||||
|
||||
# print("[+] Repackage")
|
||||
#
|
||||
# utils.replace_dex(
|
||||
# APK_NAME,
|
||||
# APK_NAME.parent / (APK_NAME.name.removesuffix(".apk") + "-instrumented.apk"),
|
||||
# dex_raw,
|
||||
# Path().parent / "my-release-key.jks",
|
||||
# zipalign=Path.home() / "Android" / "Sdk" / "build-tools" / "34.0.0" / "zipalign",
|
||||
# apksigner=Path.home() / "Android" / "Sdk" / "build-tools" / "34.0.0" / "apksigner",
|
||||
# )
|
||||
#
|
||||
print("[+] Repackage")
|
||||
|
||||
utils.replace_dex(
|
||||
APK_NAME,
|
||||
APK_NAME.parent / (APK_NAME.name.removesuffix(".apk") + "-instrumented.apk"),
|
||||
dex_raw,
|
||||
Path().parent / "my-release-key.jks",
|
||||
zipalign=Path.home() / "Android" / "Sdk" / "build-tools" / "34.0.0" / "zipalign",
|
||||
apksigner=Path.home() / "Android" / "Sdk" / "build-tools" / "34.0.0" / "apksigner",
|
||||
)
|
||||
|
||||
last_id = None
|
||||
|
||||
MAX_REQ = 1
|
||||
|
|
@ -167,8 +167,8 @@ def cmp_list(a, b, req=0):
|
|||
cmp(a[i], b[i], req + 1)
|
||||
|
||||
|
||||
apk_eq = new_apk == apk
|
||||
print(f"[+] apk are equals: {nice_bool(apk_eq)}")
|
||||
# apk_eq = new_apk == apk
|
||||
# print(f"[+] apk are equals: {nice_bool(apk_eq)}")
|
||||
# if not apk_eq:
|
||||
# cmp(new_apk, apk)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue