From 4b50504dbd12a35dac158f9802122d96b63766bb Mon Sep 17 00:00:00 2001 From: Abhi Date: Thu, 3 Oct 2024 01:37:26 +0530 Subject: [PATCH 1/7] update to androguard latest & r2 python3 --- .gitignore | 1 + extract_jni.py | 213 ++++++++++++++++++++++++----------------------- parse_dex.py | 202 ++++++++++++++++++++++++++++++++++++++++++++ r2/jni_helper.py | 26 +++--- requirements.txt | 2 +- 5 files changed, 324 insertions(+), 120 deletions(-) create mode 100644 parse_dex.py diff --git a/.gitignore b/.gitignore index 39c60ba..8630a77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.swp .DS_Store assets/ +.venv/ \ No newline at end of file diff --git a/extract_jni.py b/extract_jni.py index c4081bf..f6f6971 100755 --- a/extract_jni.py +++ b/extract_jni.py @@ -5,9 +5,11 @@ from collections import Counter, namedtuple from typing import Iterator, List -from androguard.core.analysis.analysis import Analysis -from androguard.core.bytecodes.dvm import DalvikVMFormat, ClassDefItem, EncodedMethod -from androguard.decompiler.dad.util import TYPE_DESCRIPTOR +# from androguard.core.analysis.analysis import Analysis + +from androguard.core.dex import ClassDefItem, EncodedMethod +from androguard.decompiler.util import TYPE_DESCRIPTOR +from androguard.core import dex as dx from rich.console import Console from rich.progress import ( @@ -22,31 +24,21 @@ from elftools.elf.elffile import ELFFile from elftools.elf.sections import SymbolTableSection - -DexFile = namedtuple('DexFile', ['name', 'data']) -SoFile = namedtuple('SoFile', ['name', 'data']) - +DexFile = namedtuple("DexFile", ["name", "data"]) +SoFile = namedtuple("SoFile", ["name", "data"]) JNI_COMMON = { - "JNI_OnLoad": [ - "jint", "JavaVM * vm, void * reserved" - ], - "JNI_OnUnload": [ - "void", "JavaVM * vm, void * reserved" - ], + "JNI_OnLoad": ["jint", "JavaVM * vm, void * reserved"], + "JNI_OnUnload": ["void", "JavaVM * vm, void * reserved"], } __COMMON__ = [ - { - "mangle": "JNI_OnLoad", - "ret": "jint", - "args": ["JavaVM * vm", "void * reserved"] - }, + {"mangle": "JNI_OnLoad", "ret": "jint", "args": ["JavaVM * vm", "void * reserved"]}, { "mangle": "JNI_OnUnload", "ret": "void", - "args": ["JavaVM * vm", "void * reserved"] - } + "args": ["JavaVM * vm", "void * reserved"], + }, ] @@ -56,68 +48,68 @@ def get_type(atype): """ res = TYPE_DESCRIPTOR.get(atype) if res: - if res == 'void': + if res == "void": return res else: - return 'j' + res - if atype[0] == 'L': - if atype == 'Ljava/lang/String;': - res = 'jstring' + return "j" + res + if atype[0] == "L": + if atype == "Ljava/lang/String;": + res = "jstring" else: - res = 'jobject' - elif atype[0] == '[': - if len(atype) == 2 and atype[1] in 'ZBSCIJFD': + res = "jobject" + elif atype[0] == "[": + if len(atype) == 2 and atype[1] in "ZBSCIJFD": res = TYPE_DESCRIPTOR.get(atype[1]) else: - res = 'object' - res = 'j%sArray' % res + res = "object" + res = f"j{res}Array" else: print('Unknown descriptor: "%s".', atype) - res = 'void' + res = "void" return res -def mangle_unicode(str): - out = '' - for s in str: +def mangle_unicode(input_str): + out = "" + for s in input_str: i = ord(s) - if i >= 0 and i < 128: + if 0 <= i < 128: out += s else: - out += '_0%04x' % i + out += f"_{i:04x}" return out def escape(name: str): - name = name.replace('_', '_1') - name = name.replace(';', '_2') - name = name.replace('[', '_3') + name = name.replace("_", "_1") + name = name.replace(";", "_2") + name = name.replace("[", "_3") name = mangle_unicode(name) - name = name.replace('/', '_') + name = name.replace("/", "_") return name -class JNIMethod(object): +class JNIMethod: def __init__(self, jclass, name, descriptor, static=False, overload=False): - self.jclass = jclass # fullname: e.g com.evilpan.Foo - self.name = name # method name - args, ret = descriptor[1:].rsplit(')', 1) - self.args = str(args).split() # list of smali type, space splited - self.ret = str(ret) # smali type - self.descriptor = "({}){}".format("".join(self.args), self.ret) + self.jclass = jclass # fullname: e.g com.evilpan.Foo + self.name = name # method name + method_args, ret = descriptor[1:].rsplit(")", 1) + self.args = str(method_args).split() # list of smali type, space splited + self.ret = str(ret) # smali type + self.descriptor = f"({''.join(self.args)}){self.ret}" self.static = static self.overload = overload @classmethod - def from_method(cls, em: EncodedMethod) -> 'JNIMethod': + def from_method(cls, em: EncodedMethod) -> "JNIMethod": flags = em.get_access_flags_string().split() - if 'native' not in flags: + if "native" not in flags: return None # Can be calculated this in the outside loop, but it doesn't really matters... - jclass = str(em.get_class_name()[1:-1].replace('/', '.')) + jclass = str(em.get_class_name()[1:-1].replace("/", ".")) name = str(em.name) descriptor = str(em.get_descriptor()) - return cls(jclass, name, descriptor, static='static' in flags) + return cls(jclass, name, descriptor, static="static" in flags) @property def native_name(self): @@ -125,8 +117,8 @@ def native_name(self): return crosponding native C symbol name https://docs.oracle.com/en/java/javase/16/docs/specs/jni/design.html """ - name = escape(self.jclass + '.' + self.name) - name = "Java_" + name.replace('.', '_') + name = escape(self.jclass + "." + self.name) + name = "Java_" + name.replace(".", "_") if self.overload: sig = "".join(self.args) sig = escape(sig) @@ -136,12 +128,14 @@ def native_name(self): @property def native_args(self): # NOTE: ghidra pointer and type require space inside - args = [('JNIEnv *', 'env')] + native_args_list = [("JNIEnv *", "env")] if self.static: - args.append(('jclass', 'clazz')) + native_args_list.append(("jclass", "clazz")) else: - args.append(('jobject', 'this')) - return args + [(get_type(arg), 'a%d' % (i+1)) for i, arg in enumerate(self.args)] + native_args_list.append(("jobject", "this")) + return native_args_list + [ + (get_type(arg), f"a{i + 1}") for i, arg in enumerate(self.args) + ] @property def native_args_list(self) -> List[str]: @@ -153,10 +147,7 @@ def native_ret(self): @property def as_dict(self): - return { self.native_name: [ - self.native_ret, - ", ".join(self.native_args_list) - ]} + return {self.native_name: [self.native_ret, ", ".join(self.native_args_list)]} @property def as_json(self): @@ -165,22 +156,14 @@ def as_json(self): "ret": self.native_ret, "args": self.native_args_list, "name": self.name, - "sig": self.descriptor + "sig": self.descriptor, } def __repr__(self): - return "{}{}({}){}".format( - "static " if self.static else "", - self.name, - " ".join(self.args), - self.ret, - ) + return f"{'static ' if self.static else ''}{self.name}({' '.join(self.args)}){self.ret}" def __str__(self): - return "JNIEXPORT {} JNICALL {} ({})".format( - self.native_ret, self.native_name, - ", ".join(self.native_args_list) - ) + return f"JNIEXPORT {self.native_ret} JNICALL {self.native_name} ({', '.join(self.native_args_list)})" def parse_class_def(cdef: ClassDefItem) -> List[JNIMethod]: @@ -201,45 +184,56 @@ def parse_class_def(cdef: ClassDefItem) -> List[JNIMethod]: def extract_dex_files(apkfile) -> Iterator[DexFile]: from zipfile import ZipFile + z = ZipFile(apkfile) for info in z.infolist(): - if info.filename.endswith('.dex'): + if info.filename.endswith(".dex"): yield DexFile(info.filename, z.read(info)) def get_exported_functions(filedata): out = {} elffile = ELFFile(BytesIO(filedata)) - symbol_tables = [(idx, s) for idx, s in enumerate(elffile.iter_sections()) - if isinstance(s, SymbolTableSection)] + symbol_tables = [ + (idx, s) + for idx, s in enumerate(elffile.iter_sections()) + if isinstance(s, SymbolTableSection) + ] # base = elffile.header.e_entry for section_index, section in symbol_tables: for nsym, symbol in enumerate(section.iter_symbols()): - if symbol.entry.st_info.type == 'STT_FUNC' and symbol.entry.st_shndx != 'SHN_UNDEF': - out[symbol.name] = symbol['st_value'] + if ( + symbol.entry.st_info.type == "STT_FUNC" + and symbol.entry.st_shndx != "SHN_UNDEF" + ): + out[symbol.name] = symbol["st_value"] return out def extract_so_files(apkfile: str) -> Iterator[SoFile]: from zipfile import ZipFile + z = ZipFile(apkfile) for info in z.infolist(): - if info.filename.endswith('.so') and info.filename.startswith('lib/arm64-v8a'): + if info.filename.endswith(".so") and info.filename.startswith("lib/arm64-v8a"): yield SoFile(info.filename, z.read(info)) def parse_so_sync(sofile: SoFile): funcs = get_exported_functions(sofile.data) - return {k: v for k, v in funcs.items() if k.startswith('Java_') or k in JNI_COMMON.keys()} + return {k: v for k, v in funcs.items() if k.startswith("Java_") or k in JNI_COMMON} + def parse_dex_proc(dex: DexFile): dexInfo = {} count = 0 try: - df = DalvikVMFormat(dex.data) + # Use dex.DEX to parse the DEX file + d = dx.DEX(dex.data) except Exception as e: return dex, count, e - for cdef in df.get_classes(): + + for cdef in d.get_classes(): count += 1 methods = parse_class_def(cdef) if not methods: @@ -249,6 +243,7 @@ def parse_dex_proc(dex: DexFile): dexInfo[className] = [] for m in methods: dexInfo[className].append(m.as_json) + return dex, count, dexInfo @@ -261,18 +256,16 @@ def parse_apk(apkfile, workers, fn_match=None, outfile=None): progress = Progress( TextColumn("[progress.description]{task.description}"), BarColumn( - complete_style='bar.complete', - finished_style='bar.finished', - pulse_style='bar.pulse', + complete_style="bar.complete", + finished_style="bar.finished", + pulse_style="bar.pulse", ), TextColumn("[progress.percentage]{task.percentage:>3.0f}%"), TimeRemainingColumn(), TimeElapsedColumn(), console=console, ) - dexInfo = { - "__COMMON__": __COMMON__ - } + dexInfo = {"__COMMON__": __COMMON__} num_classes = 0 t0 = datetime.now() with progress: @@ -281,19 +274,20 @@ def parse_apk(apkfile, workers, fn_match=None, outfile=None): result = pool.imap(parse_dex_proc, dexes) for dex, count, res in result: if count == 0: - console.log("Parse {} ({} bytes) [bold red]failed: {}".format( - dex.name, len(dex.data), res)) + console.log( + f"Parse {dex.name} ({len(dex.data)} bytes) [bold red]failed: {res}" + ) continue - console.log("Parse {} ({} bytes), found {} classes.".format( - dex.name, len(dex.data), count)) + console.log( + f"Parse {dex.name} ({len(dex.data)} bytes), found {count} classes." + ) num_classes += count progress.update(task, advance=len(dex.data)) for className, methodData in res.items(): if fn_match and not fn_match(className): continue dexInfo.update({className: methodData}) - console.log("Aanlyzed {} classes, cost: {}".format( - num_classes, datetime.now() - t0)) + console.log(f"Analyzed {num_classes} classes, cost: {datetime.now() - t0}") # Parse the so information synchronously, since it's fast. soInfo = {} soFiles = list(extract_so_files(apkfile)) @@ -302,25 +296,32 @@ def parse_apk(apkfile, workers, fn_match=None, outfile=None): possible_symbols = parse_so_sync(soFile) if possible_symbols: soInfo[soFile.name] = possible_symbols - console.log("Found {} JNI symbols in {}.".format( - len(possible_symbols), soFile.name)) + console.log(f"Found {len(possible_symbols)} JNI symbols in {soFile.name}.") - output = { - "dexInfo": dexInfo, - "soInfo": soInfo - } + output = {"dexInfo": dexInfo, "soInfo": soInfo} if not outfile: console.print_json(data=output) else: - with open(outfile, 'w') as f: + with open(outfile, "w") as f: json.dump(output, f, indent=2, ensure_ascii=False) -if __name__ == '__main__': +if __name__ == "__main__": import argparse - parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('apk', help='/path/to/apk') - parser.add_argument('-j', dest='workers', type=int, default=multiprocessing.cpu_count(), help='parse apk with multiple workers(processes)') - parser.add_argument('-o', dest='outfile', help='save JNI methods as formatted json file') + + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + parser.add_argument("apk", help="/path/to/apk") + parser.add_argument( + "-j", + dest="workers", + type=int, + default=multiprocessing.cpu_count(), + help="parse apk with multiple workers(processes)", + ) + parser.add_argument( + "-o", dest="outfile", help="save JNI methods as formatted json file" + ) args = parser.parse_args() - parse_apk(args.apk, args.workers, outfile=args.outfile) \ No newline at end of file + parse_apk(args.apk, args.workers, outfile=args.outfile) diff --git a/parse_dex.py b/parse_dex.py new file mode 100644 index 0000000..7552b5e --- /dev/null +++ b/parse_dex.py @@ -0,0 +1,202 @@ +# -* - coding: utf-8 -*- + +# @Credits: Androguard Team & Repo Authors +# @Repo: https://github.com/androguard/androguard.git + +from struct import pack, unpack +from binascii import hexlify + +# This is a very simple DEX parser, to get the bytecodes for each method +# Output format will be: +# +import sys + +sys.path.append(".") + +from androguard.core.dex import readuleb128, readsleb128, DalvikPacker + + +def read_null_terminated(f): + x = bytearray() + while True: + z = f.read(1) + if ord(z) == 0: + return x + else: + x.append(ord(z)) + + +class MockClassManager: + @property + def packer(self): + return DalvikPacker(0x12345678) + + +cm = MockClassManager() + + +class read_dex: + def __init__(self, fname): + methods = [] # Stores method_idx, code_off + + with open(fname, "rb") as f: + ( + magic, + checksum, + signature, + file_size, + header_size, + endian_tag, + link_size, + link_off, + map_off, + self.string_ids_size, + string_ids_off, + type_ids_size, + type_ids_off, + proto_ids_size, + proto_ids_off, + field_ids_size, + field_ids_off, + method_ids_size, + method_ids_off, + class_defs_size, + class_defs_off, + data_size, + data_off, + ) = unpack("<8sI20s20I", f.read(112)) + + # print("class_defs_size", class_defs_size, "class_defs_off", class_defs_off) + for i in range(class_defs_size): + # class_def_item + f.seek(class_defs_off + i * 8 * 4) + ( + class_idx, + access_flags, + superclass_idx, + interfaces_off, + source_file_idx, + annotations_off, + class_data_off, + static_values_off, + ) = unpack("<8I", f.read(8 * 4)) + + # Now parse the class_data_item + if class_data_off == 0: + continue + f.seek(class_data_off) + static_fields_size = readuleb128(cm, f) + instance_fields_size = readuleb128(cm, f) + direct_methods_size = readuleb128(cm, f) + virtual_methods_size = readuleb128(cm, f) + # print("class_data_item:", static_fields_size, instance_fields_size, direct_methods_size, virtual_methods_size) + + # We do not need the fields... + for _ in range(static_fields_size + instance_fields_size): + readuleb128(cm, f) + readuleb128(cm, f) + + # Now parse methods + method_idx = 0 + for _ in range(direct_methods_size): + method_idx_diff = readuleb128(cm, f) + access_flags = readuleb128(cm, f) + code_off = readuleb128(cm, f) + + # print("direct_methods", method_idx_diff, access_flags, code_off) + + method_idx += method_idx_diff + methods.append([method_idx, code_off]) + + method_idx = 0 + for _ in range(virtual_methods_size): + method_idx_diff = readuleb128(cm, f) + access_flags = readuleb128(cm, f) + code_off = readuleb128(cm, f) + + # print("virtual_methods", method_idx_diff, access_flags, code_off) + + method_idx += method_idx_diff + methods.append([method_idx, code_off]) + + # Read the string section + strings = dict() + self.str_raw = dict() + for i in range(self.string_ids_size): + f.seek(string_ids_off + i * 4) + (string_data_off,) = unpack(" 0 and insns_size % 2 == 1: + padding = unpack(" 0: + # try_item[tries_size] + tries = unpack( + "<{}".format("".join(["IHH"] * tries_size)), + f.read(8 * tries_size), + ) + + # encoded_catch_handler_list + size = readuleb128(cm, f) + for _ in range(size): + # encoded_catch_handler + s = readsleb128(cm, f) + for _ in range(abs(s)): + # encoded_type_addr_pair + _ = readuleb128(cm, f) + _ = readuleb128(cm, f) + if s <= 0: + catch_all_addr = readuleb128(cm, f) + + l = f.tell() - code_off + f.seek(code_off) + buff = f.read(l) + mres[method_idx] = hexlify(buff) + + self.methods = mres + + +if __name__ == "__main__": + for midx, buff in read_dex(sys.argv[1]).methods.items(): + pass + # print(midx, buff) diff --git a/r2/jni_helper.py b/r2/jni_helper.py index 45af7a1..90f4939 100644 --- a/r2/jni_helper.py +++ b/r2/jni_helper.py @@ -14,17 +14,17 @@ def log(fmt, *args): - print "[+]", fmt % args + print("[+]", fmt.format(args)) def load_methods(): sigfile = JNI_OUT - log("loading signature file: %s", sigfile) + log(f"loading signature file: {sigfile}") with open(sigfile, 'r') as f: infos = json.load(f) - log("loaded %d methods from JSON", len(infos)) + log(f"loaded {len(infos)} methods from JSON") return infos @@ -32,18 +32,18 @@ def apply_signature(r2, func, info): addr = func['vaddr'] name = func['name'] if info is None: - log("WARN: no info for 0x%x %s", addr, name) + log(f"WARN: no info for 0x{addr} {name}") return - log("apply 0x%x @ %s", addr, name) - r2.cmd('s %d; af' % addr) + log(f"apply 0x{addr} @ {name}") + r2.cmd(f's {addr}; af') - # formatted, but radare2 not support custom structure is sigature yet - sig = 'void %s (void* env' % name + # formatted using f-string + sig = f'void {name} (void* env' sig += ', int32_t ' + ('clazz' if info.get('isStatic') else 'thiz') for idx, at in enumerate(info.get('argumentTypes', [])): - sig += ', int32_t arg%d' % (idx + 1) + sig += f', int32_t arg{idx + 1}' sig += ');' - r2.cmd('afs ' + sig) + r2.cmd(f'afs {sig}') r2.cmd('afva') # reanalysis # workaround to add custom type for signature @@ -53,7 +53,7 @@ def apply_signature(r2, func, info): else: r2.cmd('afvt thiz jobject') for idx, at in enumerate(info.get('argumentTypes', [])): - r2.cmd('afvt arg%d %s' % ((idx+1), at)) + r2.cmd(f'afvt arg{idx + 1} {at}') r2.cmd('aft') @@ -61,8 +61,8 @@ def apply_load_unload(r2, func, unload=False): # already loaded addr = func['vaddr'] name = func['name'] - log("apply 0x%x @ %s", addr, name) - r2.cmd('s %d; af' % addr) + log(f"apply 0x{addr} @ {name}") + r2.cmd(f's {addr}; af') def main(): diff --git a/requirements.txt b/requirements.txt index b014373..bccc970 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -androguard==3.4.0a1 +androguard rich pyelftools From edafce2802c08db065b20dbf995ffd4644e6e401 Mon Sep 17 00:00:00 2001 From: Abhi Date: Thu, 3 Oct 2024 02:04:13 +0530 Subject: [PATCH 2/7] parse_dex no longer necessary * was imported for test purposes from androguard team test/ dir --- extract_jni.py | 2 +- parse_dex.py | 202 ------------------------------------------------- 2 files changed, 1 insertion(+), 203 deletions(-) delete mode 100644 parse_dex.py diff --git a/extract_jni.py b/extract_jni.py index f6f6971..4c65ca4 100755 --- a/extract_jni.py +++ b/extract_jni.py @@ -89,7 +89,7 @@ def escape(name: str): return name -class JNIMethod: +class JNIMethod(object): def __init__(self, jclass, name, descriptor, static=False, overload=False): self.jclass = jclass # fullname: e.g com.evilpan.Foo self.name = name # method name diff --git a/parse_dex.py b/parse_dex.py deleted file mode 100644 index 7552b5e..0000000 --- a/parse_dex.py +++ /dev/null @@ -1,202 +0,0 @@ -# -* - coding: utf-8 -*- - -# @Credits: Androguard Team & Repo Authors -# @Repo: https://github.com/androguard/androguard.git - -from struct import pack, unpack -from binascii import hexlify - -# This is a very simple DEX parser, to get the bytecodes for each method -# Output format will be: -# -import sys - -sys.path.append(".") - -from androguard.core.dex import readuleb128, readsleb128, DalvikPacker - - -def read_null_terminated(f): - x = bytearray() - while True: - z = f.read(1) - if ord(z) == 0: - return x - else: - x.append(ord(z)) - - -class MockClassManager: - @property - def packer(self): - return DalvikPacker(0x12345678) - - -cm = MockClassManager() - - -class read_dex: - def __init__(self, fname): - methods = [] # Stores method_idx, code_off - - with open(fname, "rb") as f: - ( - magic, - checksum, - signature, - file_size, - header_size, - endian_tag, - link_size, - link_off, - map_off, - self.string_ids_size, - string_ids_off, - type_ids_size, - type_ids_off, - proto_ids_size, - proto_ids_off, - field_ids_size, - field_ids_off, - method_ids_size, - method_ids_off, - class_defs_size, - class_defs_off, - data_size, - data_off, - ) = unpack("<8sI20s20I", f.read(112)) - - # print("class_defs_size", class_defs_size, "class_defs_off", class_defs_off) - for i in range(class_defs_size): - # class_def_item - f.seek(class_defs_off + i * 8 * 4) - ( - class_idx, - access_flags, - superclass_idx, - interfaces_off, - source_file_idx, - annotations_off, - class_data_off, - static_values_off, - ) = unpack("<8I", f.read(8 * 4)) - - # Now parse the class_data_item - if class_data_off == 0: - continue - f.seek(class_data_off) - static_fields_size = readuleb128(cm, f) - instance_fields_size = readuleb128(cm, f) - direct_methods_size = readuleb128(cm, f) - virtual_methods_size = readuleb128(cm, f) - # print("class_data_item:", static_fields_size, instance_fields_size, direct_methods_size, virtual_methods_size) - - # We do not need the fields... - for _ in range(static_fields_size + instance_fields_size): - readuleb128(cm, f) - readuleb128(cm, f) - - # Now parse methods - method_idx = 0 - for _ in range(direct_methods_size): - method_idx_diff = readuleb128(cm, f) - access_flags = readuleb128(cm, f) - code_off = readuleb128(cm, f) - - # print("direct_methods", method_idx_diff, access_flags, code_off) - - method_idx += method_idx_diff - methods.append([method_idx, code_off]) - - method_idx = 0 - for _ in range(virtual_methods_size): - method_idx_diff = readuleb128(cm, f) - access_flags = readuleb128(cm, f) - code_off = readuleb128(cm, f) - - # print("virtual_methods", method_idx_diff, access_flags, code_off) - - method_idx += method_idx_diff - methods.append([method_idx, code_off]) - - # Read the string section - strings = dict() - self.str_raw = dict() - for i in range(self.string_ids_size): - f.seek(string_ids_off + i * 4) - (string_data_off,) = unpack(" 0 and insns_size % 2 == 1: - padding = unpack(" 0: - # try_item[tries_size] - tries = unpack( - "<{}".format("".join(["IHH"] * tries_size)), - f.read(8 * tries_size), - ) - - # encoded_catch_handler_list - size = readuleb128(cm, f) - for _ in range(size): - # encoded_catch_handler - s = readsleb128(cm, f) - for _ in range(abs(s)): - # encoded_type_addr_pair - _ = readuleb128(cm, f) - _ = readuleb128(cm, f) - if s <= 0: - catch_all_addr = readuleb128(cm, f) - - l = f.tell() - code_off - f.seek(code_off) - buff = f.read(l) - mres[method_idx] = hexlify(buff) - - self.methods = mres - - -if __name__ == "__main__": - for midx, buff in read_dex(sys.argv[1]).methods.items(): - pass - # print(midx, buff) From 08774da55f00907014b938ddb22b1a2fe0581a0e Mon Sep 17 00:00:00 2001 From: Abhi Date: Thu, 3 Oct 2024 02:54:41 +0530 Subject: [PATCH 3/7] fix CI --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0e33b8d..ff0d4fb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,7 @@ jobs: - name: Build APK run: cd demo_apk && bash ./gradlew assembleDebug --stacktrace - name: Upload APK - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: demoapk path: demo_apk/app/build/outputs/apk/debug/app-debug.apk From 3aa5d552686c96fc4b0c8072afaf24d1623a6246 Mon Sep 17 00:00:00 2001 From: Abhi <85984486+AbhiTheModder@users.noreply.github.com> Date: Thu, 3 Oct 2024 23:38:54 +0530 Subject: [PATCH 4/7] the heck it's still doing here --- extract_jni.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extract_jni.py b/extract_jni.py index 4c65ca4..f6f6971 100755 --- a/extract_jni.py +++ b/extract_jni.py @@ -89,7 +89,7 @@ def escape(name: str): return name -class JNIMethod(object): +class JNIMethod: def __init__(self, jclass, name, descriptor, static=False, overload=False): self.jclass = jclass # fullname: e.g com.evilpan.Foo self.name = name # method name From 8365941e385d7d830cbc32c67e7ad51aa9e346cc Mon Sep 17 00:00:00 2001 From: Abhi <85984486+AbhiTheModder@users.noreply.github.com> Date: Thu, 3 Oct 2024 23:46:48 +0530 Subject: [PATCH 5/7] bump `download-artifact` to v4 --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ff0d4fb..9d7a560 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,7 +32,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Download APK from build - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4 with: name: demoapk - name: preinstall @@ -47,7 +47,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download APK from build - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4 with: name: demoapk - name: Create Release From 54869d63cfe6f4979d276773440e077a1e6f4066 Mon Sep 17 00:00:00 2001 From: Abhi <85984486+AbhiTheModder@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:40:39 +0530 Subject: [PATCH 6/7] CI: migrate to v4 --- .github/workflows/main.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9d7a560..8a46ab8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,7 +34,7 @@ jobs: - name: Download APK from build uses: actions/download-artifact@v4 with: - name: demoapk + pattern: demoapk - name: preinstall run: pip3 install -r requirements.txt - name: test @@ -49,7 +49,7 @@ jobs: - name: Download APK from build uses: actions/download-artifact@v4 with: - name: demoapk + pattern: demoapk - name: Create Release id: create_release uses: actions/create-release@v1 @@ -68,4 +68,3 @@ jobs: asset_path: demoapk/app-debug.apk asset_name: DemoJNI-debug.apk asset_content_type: application/zip - From 20555f2f99ae63ccf213e47d8a6602e89419d99d Mon Sep 17 00:00:00 2001 From: Abhi <85984486+AbhiTheModder@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:45:27 +0530 Subject: [PATCH 7/7] maybe fixed now? --- .github/workflows/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8a46ab8..62c8142 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,7 @@ jobs: - name: Upload APK uses: actions/upload-artifact@v3 with: - name: demoapk + name: demo.apk path: demo_apk/app/build/outputs/apk/debug/app-debug.apk test: name: Test JNI extraction @@ -34,11 +34,11 @@ jobs: - name: Download APK from build uses: actions/download-artifact@v4 with: - pattern: demoapk + pattern: demo.apk - name: preinstall run: pip3 install -r requirements.txt - name: test - run: ./extract_jni.py demoapk/app-debug.apk + run: ./extract_jni.py demo.apk release: name: release APK @@ -49,7 +49,7 @@ jobs: - name: Download APK from build uses: actions/download-artifact@v4 with: - pattern: demoapk + pattern: demo.apk - name: Create Release id: create_release uses: actions/create-release@v1