diff --git a/TODO.md b/TODO.md index e5d08b5..49ffb40 100644 --- a/TODO.md +++ b/TODO.md @@ -2,4 +2,5 @@ - tests - https://source.android.com/docs/core/runtime/dex-format#system-annotation - goto size computation +- no nop when no payload - ord in python diff --git a/tests/test.py b/tests/test.py index 856e8b7..e7a8caa 100644 --- a/tests/test.py +++ b/tests/test.py @@ -9,7 +9,7 @@ import zipfile as z import re from pathlib import Path -from androscalpel import Apk, IdType, IdMethodType, ins, DexString, IdMethod, Code, utils # type: ignore +from androscalpel import Apk, IdType, IdMethodType, ins, DexString, IdMethod, Code, utils, Field, IdField # type: ignore RED = "\033[38:2:255:0:0m" GREEN = "\033[38:2:0:255:0m" @@ -69,17 +69,17 @@ def is_evasion_method(meth: IdMethod) -> bool: print(f"[+] Code of {method_id} ") -for inst in code.insns: +for i, inst in enumerate(code.insns): match inst: case ins.InvokeVirtual(args=args, method=method) if is_evasion_method(method): - print(f" {RED}{inst}{ENDC}") + print(f"{i:>03} {RED}{inst}{ENDC}") case ins.SGetObject(to=to, field=field): - print(f" {inst}") + print(f"{i:>03} {inst}") print( f" val: {GREEN}{dyn_load_apk.classes[field.class_].static_fields[field].value}{ENDC}" ) case inst: - print(f" {inst}") + print(f"{i:>03} {inst}") malicious_class_id = IdType("Lcom/example/ut_dyn_load/SmsReceiver;") @@ -105,7 +105,7 @@ else: print(f"[+] {malicious_class_id} in loaded apk: {color}{mal_cls_in_apk}{ENDC}") print( - f"[+] -- very simplified steep, see https://developer.android.com/reference/java/lang/Class#getMethod(java.lang.String,%20java.lang.Class%3C?%3E[])" + f"[+] -- very simplified steep, see https://developer.android.com/reference/java/lang/Class#getMethod(java.lang.String,%20java.lang.Class%3C?%3E[]) --" ) name = "a" args = [ @@ -120,177 +120,52 @@ for m_id in malicious_class.direct_methods: potential_meth.append((m_id, "direct")) for m_id in malicious_class.virtual_methods: if m_id.name == name and m_id.proto.get_parameters() == args: - potential_meth.append(m_id, "virtual") + potential_meth.append((m_id, "virtual")) print("[+] Potential methods:") -for m, t in potential_meth: - print(f" {m}({t})") +for m_id, t in potential_meth: + print(f" {m_id}({t})") -exit() -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) +m_id, t = potential_meth[0] -# 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) +new_insns = ( + code.insns[:42] + + [ + ins.NewInstance(1, IdType("Lcom/example/ut_dyn_load/SmsReceiver;")), + ins.InvokeVirtual(m_id, [1, 8, 1]), + ] + + code.insns[62:] +) +print(f"[+] New code ") +for i, inst in enumerate(new_insns): + if i >= 42 and i < 44: + print(f"{i:>03} {GREEN}{inst}{ENDC}") + continue + match inst: + case ins.InvokeVirtual(args=args, method=method) if is_evasion_method(method): + print(f"{i:>03} {RED}{inst}{ENDC}") + case ins.SGetObject(to=to, field=field): + print(f"{i:>03} {inst}") + print( + f" val: {GREEN}{dyn_load_apk.classes[field.class_].static_fields[field].value}{ENDC}" + ) + case inst: + print(f"{i:>03} {inst}") -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}") +new_code = Code(code.registers_size, code.ins_size, code.outs_size, new_insns) +dyn_load_apk.set_method_code(method_id, code) -# # Strip class for debugging -# classes = list( -# filter( -# lambda x: x -# not in [ -# IdType("Lcom/example/testapplication/ui/home/HomeViewModel;"), -# IdType("Landroidx/navigation/NavDeepLink$Builder;"), -# IdType("Landroidx/constraintlayout/core/widgets/ConstraintWidget$1;"), -# IdType("Landroidx/appcompat/app/ActionBar;"), -# IdType("Landroidx/constraintlayout/core/state/WidgetFrame;"), -# IdType("Landroidx/appcompat/app/AppCompatViewInflater;"), -# ], -# apk.classes.keys(), -# ) -# ) -# 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) - +dex_raw = dyn_load_apk.gen_raw_dex() print("[+] Repackage") - utils.replace_dex( - APK_NAME, - APK_NAME.parent / (APK_NAME.name.removesuffix(".apk") + "-instrumented.apk"), + DYN_LOAD_APK, + DYN_LOAD_APK.parent + / (DYN_LOAD_APK.name.removesuffix(".apk") + "-instrumented.apk"), dex_raw, - Path().parent / "my-release-key.jks", + Path(__file__).parent.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 - - -def cmp(a, b, req=0): - if req > MAX_REQ: - return - if type(a) == dict: - cmp_dict(a, b, req) - elif type(a) == list: - cmp_list(a, b, req) - else: - cmp_other(a, b, req) - - -def nice_bool(b) -> str: - if b: - return "\033[32mTrue\033[0m" - else: - return "\033[31mFalse\033[0m" - - -def cmp_other(a, b, req=0): - ident = " " * req - for f in dir(a): - if getattr(getattr(a, f), "__call__", None) is None and ( - len(f) < 2 or f[:2] != "__" - ): - eq = getattr(a, f) == getattr(b, f) - print(f"{f'{ident}{f}: ':<150}{nice_bool(eq)}") - if not eq: - if "descriptor" in dir(a): - global last_id - last_id = a.descriptor - cmp(getattr(a, f), getattr(b, f), req + 1) - - -def cmp_dict(a, b, req=0): - ident = " " * req - keys_a = set(a.keys()) - keys_b = set(b.keys()) - if keys_a != keys_b: - print(f"{ident}a.keys() != b.keys()") - tot = 0 - nb_failed = 0 - for key in keys_a & keys_b: - eq = a[key] == b[key] - tot += 1 - if not eq: - nb_failed += 1 - print(f"{f'{ident}{str(key)}: ':<150}{nice_bool(eq)}") - global last_id - last_id = key - cmp(a[key], b[key], req + 1) - print(f"\033[32m{tot-nb_failed}\033[0m + \033[31m{nb_failed}\033[0m = {tot}") - - -def cmp_list(a, b, req=0): - ident = " " * req - la = len(a) - lb = len(b) - if la != lb: - print(f"{ident}len(a) != len(b)") - for i in range(min(la, lb)): - eq = a[i] == b[i] - print(f"{f'{ident}{str(i)}: ':<150}{nice_bool(eq)}") - if not eq: - cmp(a[i], b[i], req + 1) - - -# 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( -# "", -# IdMethodType( -# IdType.void(), -# [], -# ), -# IdType("Landroidx/constraintlayout/core/widgets/ConstraintWidget$1;"), -# ) -# 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;"), -# ) -# -# m = apk.classes[mid.class_].virtual_methods[mid] -# nm = new_apk.classes[mid.class_].virtual_methods[mid] -# c = m.code -# nc = nm.code -# cc = c.with_normalized_labels() -# ncc = nc.with_normalized_labels()