Skip to content

Some improvements on code analysis and usability. #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion add_dart_objects_in_decompiled_code.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import sys, os
import idaapi, ida_kernwin
from get_options import get_options

try:
import flure
Expand All @@ -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()
Expand Down
9 changes: 8 additions & 1 deletion add_xref_to_dart_objects.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import sys, os
import idaapi, ida_kernwin
from get_options import get_options

try:
import flure
except ImportError:
Expand All @@ -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)
9 changes: 8 additions & 1 deletion create_dart_objects.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import sys, os
import idaapi, ida_kernwin
from get_options import get_options

try:
import flure
except ImportError:
Expand All @@ -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()
2 changes: 1 addition & 1 deletion flure/ida/dart_obj_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
119 changes: 108 additions & 11 deletions flure/ida/dart_obj_xref.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <reg_tmp>, X27, #0x<index_high>,LSL#<index_high_shift>
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 <reg_dst>, [<reg_tmp>,#0x<index_low>]
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 <reg_dst>, [X27,#0x<index>]
Expand All @@ -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)
Expand All @@ -53,6 +59,90 @@ def get_dart_object_index_pattern_2(addr):
return obj_index


# ADD <reg_tmp>, X27, #0x<index_high>
RE_ADD_X27_PATTERN2 = re.compile(r"ADD (\S*), X27, #([x0-9a-fA-F]+)")

# ADD <reg_tmp>, <reg_tmp>, #0x<index_low>
RE_ADD_TMP_PATTERN = re.compile(r"ADD (\S*), (\S*), #(\S+)")

# LDP reg_dst_1, reg_dst_2, [<reg_tmp>,#<index_low>]
RE_LDP_AFTER_ADD_PATTERN = re.compile(r"LDP (\S*), (\S*), \[(\S*)\,#(\S*)\]")

# LDP reg_dst_1, reg_dst_2, [<reg_tmp>]
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)
Expand All @@ -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


Expand Down
15 changes: 15 additions & 0 deletions get_options.py
Original file line number Diff line number Diff line change
@@ -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
42 changes: 39 additions & 3 deletions hooking/dump_flutter_memory.js
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
35 changes: 28 additions & 7 deletions map_dart_vm_memory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import sys, os
import idaapi, ida_kernwin, ida_segment
from get_options import get_options

try:
import flure
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)