diff --git a/add_dart_objects_in_decompiled_code.py b/add_dart_objects_in_decompiled_code.py index 45ba69c..d6d1301 100644 --- a/add_dart_objects_in_decompiled_code.py +++ b/add_dart_objects_in_decompiled_code.py @@ -1,5 +1,6 @@ import sys, os import idaapi, ida_kernwin +from get_options import get_options try: import flure @@ -16,7 +17,11 @@ except NameError as e: pass finally: - object_pool_address = ida_kernwin.ask_addr(0x7200600040, "Please enter Object Pool address") + object_pool_address = get_options().get("object_pool", None) + if object_pool_address is None: + object_pool_address = ida_kernwin.ask_addr(0x7200600040, "Please enter Object Pool address") + else: + object_pool_address = int(object_pool_address, 0) if object_pool_address is not None: flure.ida.object_pool_microcode.OBJECT_POOL_PTR = object_pool_address x27_replace_hook = X27ReplaceHook() diff --git a/add_xref_to_dart_objects.py b/add_xref_to_dart_objects.py index ce27ffa..d80f7bc 100644 --- a/add_xref_to_dart_objects.py +++ b/add_xref_to_dart_objects.py @@ -1,5 +1,7 @@ import sys, os import idaapi, ida_kernwin +from get_options import get_options + try: import flure except ImportError: @@ -10,6 +12,11 @@ if __name__ == "__main__": - object_pool_address = ida_kernwin.ask_addr(0x7200600040, "Please enter Object Pool address") + object_pool_address = get_options().get("object_pool", None) + if object_pool_address is None: + object_pool_address = ida_kernwin.ask_addr(0x7200600040, "Please enter Object Pool address") + else: + object_pool_address = int(object_pool_address, 0) + if object_pool_address is not None: parse_code_and_add_dart_object_xref(object_pool_address) diff --git a/create_dart_objects.py b/create_dart_objects.py index ad209ca..68f73c6 100644 --- a/create_dart_objects.py +++ b/create_dart_objects.py @@ -1,5 +1,7 @@ import sys, os import idaapi, ida_kernwin +from get_options import get_options + try: import flure except ImportError: @@ -10,7 +12,12 @@ if __name__ == "__main__": - object_pool_address = ida_kernwin.ask_addr(0x7200600040, "Please enter Object Pool address") + object_pool_address = get_options().get("object_pool", None) + if object_pool_address is None: + object_pool_address = ida_kernwin.ask_addr(0x7200600040, "Please enter Object Pool address") + else: + object_pool_address = int(object_pool_address, 0) + if object_pool_address is not None: dart_object_creator = DartObjectsCreator(object_pool_address) dart_object_creator.create_all_objects() diff --git a/flure/ida/dart_obj_create.py b/flure/ida/dart_obj_create.py index 7053b2c..a5ad097 100644 --- a/flure/ida/dart_obj_create.py +++ b/flure/ida/dart_obj_create.py @@ -40,7 +40,7 @@ def create_ida_struct(self, cid): idc.add_struc_member(x, "size_tag", -1, (idc.FF_BYTE | idc.FF_DATA) & 0xFFFFFFFF, -1, 1) idc.add_struc_member(x, "cid", -1, (idc.FF_WORD | idc.FF_DATA) & 0xFFFFFFFF, -1, 2) idc.add_struc_member(x, "padding", -1, (idc.FF_DWORD | idc.FF_DATA) & 0xFFFFFFFF, -1, 4) - idc.add_struc_member(x, "unk", -1, (idc.FF_QWORD | idc.FF_DATA) & 0xFFFFFFFF, -1, 8) + idc.add_struc_member(x, "unk_offs", -1, (idc.FF_QWORD | idc.FF_0OFF | idc.FF_DATA) & 0xFFFFFFFF, -1, 8, -1, 0, idc.REF_OFF64) def get_struct_len(self): return -1 diff --git a/flure/ida/dart_obj_xref.py b/flure/ida/dart_obj_xref.py index f2dcc1f..809df78 100644 --- a/flure/ida/dart_obj_xref.py +++ b/flure/ida/dart_obj_xref.py @@ -6,17 +6,19 @@ import ida_bytes, ida_name, ida_offset -LOAD_X27_PATTERN = "\[X27,#0x(\S*)\]" +LOAD_X27_PATTERN = r"\[X27,#0x(\S*)\]" RE_LOAD_X27_PATTERN = re.compile(LOAD_X27_PATTERN) # ADD , X27, #0x,LSL# -ADD_X27_PATTERN = "ADD (\S*), X27, #0x(\S*),LSL#(\S*)" +ADD_X27_PATTERN = r"ADD (\S*), X27, #(\S*),LSL#(\d+)" RE_ADD_X27_PATTERN = re.compile(ADD_X27_PATTERN) # LDR , [,#0x] -LDR_AFTER_ADD_PATTERN = "LDR (\S*), \[(\S*),#0x(\S*)\]" +LDR_AFTER_ADD_PATTERN = r"LDR (\S*), \[(\S*),#0x(\S*)\]" RE_LDR_AFTER_ADD_PATTERN = re.compile(LDR_AFTER_ADD_PATTERN) +LDR_AFTER_ADD_PATTERN2 = r"LDR (\S*), \[(\S*)\]" +RE_LDR_AFTER_ADD_PATTERN2 = re.compile(LDR_AFTER_ADD_PATTERN2) def get_dart_object_index_pattern_1(addr): # LDR , [X27,#0x] @@ -39,11 +41,15 @@ def get_dart_object_index_pattern_2(addr): if not add_match_info: return None disasm_line = idc.generate_disasm_line(idc.next_head(addr), 0) - ldr_match_info = RE_LDR_AFTER_ADD_PATTERN.match(disasm_line) - if not ldr_match_info: + + if ldr_match_info := RE_LDR_AFTER_ADD_PATTERN.match(disasm_line): + dst_reg, tmp_reg_2, index_low = ldr_match_info.group(1), ldr_match_info.group(2), ldr_match_info.group(3) + elif ldr_match_info := RE_LDR_AFTER_ADD_PATTERN2.match(disasm_line): + dst_reg, tmp_reg_2, index_low = ldr_match_info.group(1), ldr_match_info.group(2), "0" + else: return None + tmp_reg, index_high, index_shift = add_match_info.group(1), add_match_info.group(2), add_match_info.group(3) - dst_reg, tmp_reg_2, index_low = ldr_match_info.group(1), ldr_match_info.group(2), ldr_match_info.group(3) if tmp_reg != tmp_reg_2: return None index_high = int(index_high, 16) @@ -53,6 +59,90 @@ def get_dart_object_index_pattern_2(addr): return obj_index +# ADD , X27, #0x +RE_ADD_X27_PATTERN2 = re.compile(r"ADD (\S*), X27, #([x0-9a-fA-F]+)") + +# ADD , , #0x +RE_ADD_TMP_PATTERN = re.compile(r"ADD (\S*), (\S*), #(\S+)") + +# LDP reg_dst_1, reg_dst_2, [,#] +RE_LDP_AFTER_ADD_PATTERN = re.compile(r"LDP (\S*), (\S*), \[(\S*)\,#(\S*)\]") + +# LDP reg_dst_1, reg_dst_2, [] +RE_LDP_AFTER_ADD_PATTERN2 = re.compile(r"LDP (\S*), (\S*), \[(\S*)\]") + + +def get_dart_object_index_pattern_3(addr): + """ + 70FD44BC9C + ADD X16, X27, #0x750 + LDP X5, X30, [X16] + + and + + 70FD44BB70 + ADD X16, X27, #7,LSL#12 + ADD X16, X16, #0x7D0 + LDP X5, X30, [X16] + + and + + 70FD450624 + ADD X16, X27, #0xE,LSL#12 + LDP X5, X30, [X16,#0x1E0] + """ + instructions_count = 2 # count of instructions in matched pattern + disasm_line = idc.generate_disasm_line(addr, 0) + add_match_info = RE_ADD_X27_PATTERN.match(disasm_line) + + if not add_match_info: + add_match_info = RE_ADD_X27_PATTERN2.match(disasm_line) + if not add_match_info: + return None + else: + tmp_reg, index_high, index_shift = add_match_info.group(1), add_match_info.group(2).replace(";", ""), "0" + else: + tmp_reg, index_high, index_shift = add_match_info.group(1), add_match_info.group(2).replace(";",""),\ + add_match_info.group(3).replace(";", "") + + # for the case when there is one more add + addr = idc.next_head(addr) + disasm_line = idc.generate_disasm_line(addr, 0) + add_match_info_2 = RE_ADD_TMP_PATTERN.match(disasm_line) + + if add_match_info_2: + instructions_count += 1 + addr = idc.next_head(addr) + + disasm_line = idc.generate_disasm_line(addr, 0) + ldp_match_info = RE_LDP_AFTER_ADD_PATTERN.match(disasm_line) or RE_LDP_AFTER_ADD_PATTERN2.match(disasm_line) + + if not ldp_match_info: + return None + + dst_reg, dst_reg1, tmp_reg_2 = ldp_match_info.group(1), ldp_match_info.group(2), ldp_match_info.group(3) + + if tmp_reg != tmp_reg_2: + return None + + if len(ldp_match_info.groups()) > 3: + index_low = ldp_match_info.group(4) + else: + index_low = "0" + + index_low_2 = 0 + if add_match_info_2: + dst_reg_2, src_reg_2, delta_2 = add_match_info_2.group(1), add_match_info_2.group(2), add_match_info_2.group(3) + index_low_2 = int(delta_2, 0) + + index_high = int(index_high, 0) + index_low = int(index_low, 0) + index_shift = int(index_shift, 0) + obj_index = (index_high << index_shift) + index_low + index_low_2 + + return instructions_count, obj_index, obj_index + 8 + + def add_dart_object_xref(addr, object_pool_ptr, dart_object_index): dart_obj_ptr_ptr = object_pool_ptr + dart_object_index dart_obj_ptr = idc.get_qword(dart_obj_ptr_ptr) @@ -63,13 +153,20 @@ def add_dart_object_xref(addr, object_pool_ptr, dart_object_index): def check_and_add_dart_object_xref(addr, object_pool_ptr): - dart_object_index = get_dart_object_index_pattern_1(addr) - if dart_object_index is None: - dart_object_index = get_dart_object_index_pattern_2(addr) + if dart_object_index := get_dart_object_index_pattern_1(addr): + add_dart_object_xref(addr, object_pool_ptr, dart_object_index) + elif dart_object_index := get_dart_object_index_pattern_2(addr): addr = idc.next_head(addr) - if dart_object_index is None: + add_dart_object_xref(addr, object_pool_ptr, dart_object_index) + elif dart_object_index := get_dart_object_index_pattern_3(addr): + instructions_count = dart_object_index[0] - 1 + while instructions_count > 0: + addr = idc.next_head(addr) + instructions_count -= 1 + add_dart_object_xref(addr, object_pool_ptr, dart_object_index[1]) + add_dart_object_xref(addr, object_pool_ptr, dart_object_index[2]) + else: return False - add_dart_object_xref(addr, object_pool_ptr, dart_object_index) return True diff --git a/get_options.py b/get_options.py new file mode 100644 index 0000000..66eeb66 --- /dev/null +++ b/get_options.py @@ -0,0 +1,15 @@ +import json +import os + +f_name = "dump_info.json" + + +def get_options(): + if not os.path.isfile(f_name): + return {} + + with open(f_name, "rt") as fi: + options = json.load(fi) + + print(f"got options: {json.dumps(options, indent=4)}") + return options diff --git a/hooking/dump_flutter_memory.js b/hooking/dump_flutter_memory.js index 9cb7122..c3ec5da 100644 --- a/hooking/dump_flutter_memory.js +++ b/hooking/dump_flutter_memory.js @@ -4,38 +4,74 @@ var FLUTTER_MEM_MASK = 0xff00000000 var SHARED_PREF_GET_INSTANCE_OFFSET = 0x6D4F88 var APP_DATA_DIR = "/data/data/fr.carameldunes.nyanyarocket/" +var flutter_map = { + "object_pool": 0, + "libapp_base": 0, + "dump_ro": 0, + "dump_rw": 0, + "memory_dumps": [] +}; -function dump_memory(start_address, end_address, dump_directory){ +var already_dumped = false; + +function dump_memory(start_address, end_address, dump_directory) { let modules = Process.enumerateRanges("r--"); - let i, module; + let i, module, k; let module_file; + let map_file; + + if (already_dumped) { + return; + } module_file = new File(dump_directory + "ranges.json", "wb"); module_file.write(JSON.stringify(modules, null, 2)); module_file.close(); + + k = 0 for (i = 0; i < modules.length; i++) { try { module = modules[i]; if ((module.base.compare(start_address) >= 0) && (module.base.compare(end_address) <= 0)) { - console.log(`Dumping memory into ${dump_directory + module.base}`); + console.log(`Dumping ${i} memory into ${dump_directory + module.base}`); module_file = new File(dump_directory + module.base, "wb"); module_file.write(module.base.readByteArray(module.size)); module_file.close(); + + if (k == 0) { + flutter_map["dump_ro"] = module.base; + k++; + } else if (k == 1) { + flutter_map["dump_rw"] = module.base; + } + flutter_map["memory_dumps"].push(module.base) } } catch(ex) { console.log(ex); console.log(JSON.stringify(module, null, 2)); } } + + map_file = new File(dump_directory + "dump_info.json", "wt"); + map_file.write(JSON.stringify(flutter_map, null, 2)); + map_file.close(); + + already_dumped = true; } function hook_libapp() { var base_address = Module.findBaseAddress("libapp.so"); console.log(`Hooking libapp: ${base_address} `); + flutter_map["libapp_base"] = `${base_address}`; + Interceptor.attach(base_address.add(SHARED_PREF_GET_INSTANCE_OFFSET), { onEnter: function (args) { + if (already_dumped) { + return; + } console.log(`SharedPreferences::getInstance() `); console.log(` X27: ${this.context.x27}`) + flutter_map["object_pool"] = `${this.context.x27}` if (this.context.x27.and(FLUTTER_MEM_MASK) == FLUTTER_MEM_START){ dump_memory(FLUTTER_MEM_START, FLUTTER_MEM_END, APP_DATA_DIR) }else{ diff --git a/map_dart_vm_memory.py b/map_dart_vm_memory.py index cab0897..572a932 100644 --- a/map_dart_vm_memory.py +++ b/map_dart_vm_memory.py @@ -1,5 +1,6 @@ import sys, os import idaapi, ida_kernwin, ida_segment +from get_options import get_options try: import flure @@ -10,9 +11,12 @@ from flure.ida.create_segment import add_dartvm_segment if __name__ == "__main__": - ro_memory_file_name = ida_kernwin.ask_file(False, - f"/Users/boris/Desktop/demo_flutter/obfu/dump_mem/0x7200000000", - "Flutter RO memory filename") + options = get_options() + ro_memory_file_name = options.get("dump_ro", None) + if ro_memory_file_name is None: + ro_memory_file_name = ida_kernwin.ask_file(False, + "/Users/boris/Desktop/demo_flutter/obfu/dump_mem/0x7200000000", + "Flutter RO memory filename") if ro_memory_file_name is not None: try: guessed_ro_memory_address = int(os.path.basename(ro_memory_file_name), 16) @@ -24,9 +28,11 @@ add_dartvm_segment(ro_memory_address, "flutter_ro", ida_segment.SEGPERM_READ, ro_memory_file_name) - rw_memory_file_name = ida_kernwin.ask_file(False, - f"/Users/boris/Desktop/demo_flutter/obfu/dump_mem/0x7200080000", - "Flutter RW memory filename") + rw_memory_file_name = options.get("dump_rw", None) + if rw_memory_file_name is None: + rw_memory_file_name = ida_kernwin.ask_file(False, + "/Users/boris/Desktop/demo_flutter/obfu/dump_mem/0x7200080000", + "Flutter RW memory filename") if rw_memory_file_name is not None: try: guessed_rw_memory_address = int(os.path.basename(rw_memory_file_name), 16) @@ -38,4 +44,19 @@ add_dartvm_segment(rw_memory_address, "flutter_rw", ida_segment.SEGPERM_WRITE | ida_segment.SEGPERM_READ, rw_memory_file_name) - + if len(options.get("memory_dumps", [])) > 2: + mapped_regions = [os.path.basename(ro_memory_file_name), os.path.basename(rw_memory_file_name)] + for i, dump_file_name in enumerate(options["memory_dumps"]): + f_name = os.path.basename(dump_file_name) + if f_name in mapped_regions: + continue + try: + memory_address = int(f_name, 16) + except ValueError: + memory_address = ida_kernwin.ask_addr(0, + f"Please enter Flutter additional {i} memory address" + f"with name {f_name}") + if memory_address is not None: + print(f"Mapping {dump_file_name} at 0x{memory_address:x}") + add_dartvm_segment(memory_address, f"flutter_{i}", ida_segment.SEGPERM_WRITE | ida_segment.SEGPERM_READ, + dump_file_name)