From 4e1c36ad3cca3180ad77814038eaccef4e64a2de Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Mon, 26 Feb 2024 14:27:53 +0100 Subject: [PATCH] wip --- TODO.md | 2 + androscalpel/src/apk.rs | 15 ++++ androscalpel/src/code.rs | 15 ++-- test.py | 144 ++++++++++++++++++++++++--------------- 4 files changed, 115 insertions(+), 61 deletions(-) diff --git a/TODO.md b/TODO.md index 49ffb40..940aa4b 100644 --- a/TODO.md +++ b/TODO.md @@ -3,4 +3,6 @@ - https://source.android.com/docs/core/runtime/dex-format#system-annotation - goto size computation - no nop when no payload +- option to get label at every code addresses +- name register / parameters - ord in python diff --git a/androscalpel/src/apk.rs b/androscalpel/src/apk.rs index c609003..693037f 100644 --- a/androscalpel/src/apk.rs +++ b/androscalpel/src/apk.rs @@ -27,6 +27,8 @@ pub struct Apk { pub not_referenced_strings: HashSet, } +const LABEL_EACH_INST: bool = true; + impl Apk { /// Add the content of a dex file to the apk. pub fn add_dex_file(&mut self, data: &[u8]) -> Result<()> { @@ -756,6 +758,10 @@ impl Apk { use crate::instructions::*; use InsFormat::*; let mut labels = HashMap::new(); + if LABEL_EACH_INST { + let label = format!("label_{addr:08X}"); + labels.insert(addr, label.clone()); + } let ins = match format.clone() { Format10X { op: 0x00 } => Instruction::Nop(Nop::new()), Format12X { op: 0x01, va, vb } => Instruction::Move(Move::new(va as u16, vb as u16)), @@ -2382,6 +2388,15 @@ impl Apk { self.add_dex_file(data) } + pub fn add_class(&mut self, class: Class) -> Result<()> { + let id = class.descriptor.clone(); + if self.classes.get(&id).is_some() { + bail!("class {} already exists in the apk", id.__str__()); + } + self.classes.insert(id, class); + Ok(()) + } + pub fn set_method_code(&mut self, method_id: IdMethod, code: Option) -> Result<()> { let class = self .classes diff --git a/androscalpel/src/code.rs b/androscalpel/src/code.rs index be3aa9f..94d1dae 100644 --- a/androscalpel/src/code.rs +++ b/androscalpel/src/code.rs @@ -44,8 +44,8 @@ pub struct Code { impl PartialEq for Code { fn eq(&self, other: &Self) -> bool { - let comparable_self = self.with_normalized_labels().unwrap(); - let comparable_other = other.with_normalized_labels().unwrap(); + let comparable_self = self.semantic_comparable().unwrap(); + let comparable_other = other.semantic_comparable().unwrap(); (comparable_self.registers_size == comparable_other.registers_size) && (comparable_self.ins_size == comparable_other.ins_size) && (comparable_self.outs_size == comparable_other.outs_size) @@ -228,10 +228,12 @@ impl Code { used_labels } - /// Generate a new code with normalized labels. - /// This allows to compare codes with same semantic but different labels. + /// Generate a new code with normalized labels and no nops. + /// This allows to compare codes with same semantic but different labels + /// and padding. /// (ig when the same code was reserialized) - pub fn with_normalized_labels(&self) -> Result { + pub fn semantic_comparable(&self) -> Result { + // TODO: keep the nops that are not used for padding let used_labels = self.get_referenced_label(); let mut new_labels = HashMap::new(); let mut label_id = 0; @@ -456,7 +458,7 @@ impl Code { } Instruction::Label(ins::Label { name }) => { if used_labels.get(&name).is_none() { - println!("{name} not used"); + //println!("{name} not used"); continue; } if !last_ins_was_a_label { @@ -472,6 +474,7 @@ impl Code { } last_ins_was_a_label = true; } + Instruction::Nop(_) => (), instr => { last_ins_was_a_label = false; new_insns.push(instr); diff --git a/test.py b/test.py index 9da62a7..84ac8b4 100644 --- a/test.py +++ b/test.py @@ -22,44 +22,44 @@ 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) +# 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) +# 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}") +# 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 +# Strip class for debugging # classes = list( # filter( # lambda x: x @@ -77,29 +77,29 @@ for i in code.insns: # for cls in classes: # apk.remove_class(cls) # -print("[+] Recompile") - -dex_raw = apk.gen_raw_dex() - -new_apk = Apk() -for dex in dex_raw: - new_apk.add_dex_file(dex) +# print("[+] Recompile") +# +# dex_raw = apk.gen_raw_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 +MAX_REQ = 5 def cmp(a, b, req=0): @@ -168,11 +168,45 @@ def cmp_list(a, b, req=0): cmp(a[i], b[i], req + 1) +c1_id = IdType("Lcom/example/testapplication/ui/home/HomeViewModel;") +c2_id = IdType("Landroidx/navigation/NavDeepLink$Builder;") +c1 = apk.classes[c1_id] +c2 = apk.classes[c2_id] + +apk_1 = Apk() +apk_2 = Apk() +apk_1_2 = Apk() +apk_1_then_2 = Apk() +apk_2_then_1 = Apk() +apk_then_1_2 = Apk() + +apk_1.add_class(c1) +apk_2.add_class(c2) +apk_1_2.add_class(c1) +apk_1_2.add_class(c2) + +dex_1 = apk_1.gen_raw_dex()[0] +dex_2 = apk_2.gen_raw_dex()[0] +dex_1_2 = apk_1_2.gen_raw_dex()[0] + +apk_1_then_2.add_dex_file(dex_1) +apk_1_then_2.add_dex_file(dex_2) +apk_2_then_1.add_dex_file(dex_2) +apk_2_then_1.add_dex_file(dex_1) +apk_then_1_2.add_dex_file(dex_1_2) + +cmp(c1, apk_1_then_2.classes[c1_id]) +cmp(c1, apk_2_then_1.classes[c1_id]) +cmp(c1, apk_then_1_2.classes[c1_id]) +cmp(c2, apk_1_then_2.classes[c2_id]) +cmp(c2, apk_2_then_1.classes[c2_id]) +cmp(c2, apk_then_1_2.classes[c2_id]) + # apk_eq = new_apk == apk # print(f"[+] apk are equals: {nice_bool(apk_eq)}") # if not apk_eq: # cmp(new_apk, apk) - +# # Landroidx/constraintlayout/core/widgets/ConstraintWidget$1;.()V # mid = IdMethod( # "", @@ -184,18 +218,18 @@ def cmp_list(a, b, req=0): # ) # m = apk.classes[mid.class_].direct_methods[mid] # nm = new_apk.classes[mid.class_].direct_methods[mid] - - +# +# # mid = IdMethod( # "setValue", # IdMethodType( # IdType("Z"), # [ # IdType("Ljava/lang/String;"), -# IdType("Landroidx/constraintlayout/core/parser/CLElement;"), -# ], -# ), -# IdType("Landroidx/constraintlayout/core/state/WidgetFrame;"), +# IdType("Landroidx/constraintlayout/core/parser/CLElement;"), +# ], +# ), +# IdType("Landroidx/constraintlayout/core/state/WidgetFrame;"), # ) # # m = apk.classes[mid.class_].virtual_methods[mid]