Skip to content
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

code cleanup, revert comment and add new flag #12

Merged
merged 11 commits into from
Sep 25, 2024
54 changes: 41 additions & 13 deletions blutter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ def __init__(
rebuild_blutter: bool,
create_vs_sln: bool,
no_analysis: bool,
ida_fcn: bool,
):
self.libapp_path = libapp_path
self.dart_info = dart_info
self.outdir = outdir
self.rebuild_blutter = rebuild_blutter
self.create_vs_sln = create_vs_sln
self.ida_fcn = ida_fcn

vers = dart_info.version.split(".", 2)
if int(vers[0]) == 2 and int(vers[1]) < 15:
Expand All @@ -51,6 +53,8 @@ def __init__(
self.name_suffix += "_no-compressed-ptrs"
if no_analysis:
self.name_suffix += "_no-analysis"
if ida_fcn:
self.name_suffix += "_ida-fcn"
# derive blutter executable filename
self.blutter_name = f"blutter_{dart_info.lib_name}{self.name_suffix}"
self.blutter_file = os.path.join(BIN_DIR, self.blutter_name) + (
Expand Down Expand Up @@ -90,7 +94,7 @@ def extract_libs_from_apk(apk_file: str, out_dir: str):
return app_file, flutter_file


def find_compat_macro(dart_version: str, no_analysis: bool):
def find_compat_macro(dart_version: str, no_analysis: bool, ida_fcn: bool):
macros = []
include_path = os.path.join(PKG_INC_DIR, f"dartvm{dart_version}")
vm_path = os.path.join(include_path, "vm")
Expand Down Expand Up @@ -139,19 +143,25 @@ def find_compat_macro(dart_version: str, no_analysis: bool):
if no_analysis:
macros.append("-DNO_CODE_ANALYSIS=1")

if dart_version >= "3.5.0":
# [vm] marking_stack_block_offset() changes in Dart Stable 3.5.0
# https://github.com/worawit/blutter/issues/96#issue-2470674670
macros.append("-DOLD_MARKING_STACK_BLOCK=1")

if ida_fcn:
macros.append("-DIDA_FCN=1")

d_v = float('.'.join(dart_version.split('.')[:2]))
if d_v >= float(3.5) :
with open(os.path.join(vm_path, "compiler", "runtime_api.h"), "rb") as f:
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
if not mm.find(b" old_marking_stack_block_offset") == -1:
# [vm] marking_stack_block_offset() changes since Dart Stable 3.5.0
# https://github.com/worawit/blutter/issues/96#issue-2470674670
macros.append("-DOLD_MARKING_STACK_BLOCK=1")
return macros


def cmake_blutter(input: BlutterInput):
blutter_dir = os.path.join(SCRIPT_DIR, "blutter")
builddir = os.path.join(BUILD_DIR, input.blutter_name)

macros = find_compat_macro(input.dart_info.version, input.no_analysis)
macros = find_compat_macro(input.dart_info.version, input.no_analysis, input.ida_fcn)

my_env = None
if platform.system() == "Darwin":
Expand Down Expand Up @@ -222,7 +232,7 @@ def build_and_run(input: BlutterInput):

# creating Visual Studio solution overrides building
if input.create_vs_sln:
macros = find_compat_macro(dart_version, no_analysis)
macros = find_compat_macro(input.dart_info.version, input.no_analysis, input.ida_fcn)
blutter_dir = os.path.join(SCRIPT_DIR, "blutter")
dbg_output_path = os.path.abspath(os.path.join(input.outdir, "out"))
dbg_cmd_args = f"-i {input.libapp_path} -o {dbg_output_path}"
Expand Down Expand Up @@ -269,11 +279,12 @@ def main_no_flutter(
rebuild_blutter: bool,
create_vs_sln: bool,
no_analysis: bool,
ida_fcn: bool,
):
version, os_name, arch = dart_version.split("_")
dart_info = DartLibInfo(version, os_name, arch)
input = BlutterInput(
libapp_path, dart_info, outdir, rebuild_blutter, create_vs_sln, no_analysis
libapp_path, dart_info, outdir, rebuild_blutter, create_vs_sln, no_analysis, ida_fcn
)
build_and_run(input)

Expand All @@ -285,10 +296,11 @@ def main2(
rebuild_blutter: bool,
create_vs_sln: bool,
no_analysis: bool,
ida_fcn: bool,
):
dart_info = get_dart_lib_info(libapp_path, libflutter_path)
input = BlutterInput(
libapp_path, dart_info, outdir, rebuild_blutter, create_vs_sln, no_analysis
libapp_path, dart_info, outdir, rebuild_blutter, create_vs_sln, no_analysis, ida_fcn
)
build_and_run(input)

Expand All @@ -299,6 +311,7 @@ def main(
rebuild_blutter: bool,
create_vs_sln: bool,
no_analysis: bool,
ida_fcn: bool,
):
if indir.endswith(".apk"):
with tempfile.TemporaryDirectory() as tmp_dir:
Expand All @@ -310,6 +323,7 @@ def main(
rebuild_blutter,
create_vs_sln,
no_analysis,
ida_fcn,
)
else:
libapp_file, libflutter_file = find_lib_files(indir)
Expand All @@ -321,6 +335,7 @@ def main(
rebuild_blutter,
create_vs_sln,
no_analysis,
ida_fcn,
)


Expand Down Expand Up @@ -386,13 +401,25 @@ def check_for_updates_and_pull():
"--dart-version",
help='Run without libflutter (indir become libapp.so) by specify dart version such as "3.4.2_android_arm64"',
)
parser.add_argument(
"--nu",
action="store_false",
default=True,
help="Don't check for updates",
)
parser.add_argument(
"--ida-fcn",
action="store_true",
default=False,
help="Generate IDA function names script, Doesn't Generates Thread and Object Pool structs comments",
)
args = parser.parse_args()

# Check for updates and pull them if necessary
check_for_updates_and_pull()
if args.nu:
check_for_updates_and_pull()

if args.dart_version is None:
main(args.indir, args.outdir, args.rebuild, args.vs_sln, args.no_analysis)
main(args.indir, args.outdir, args.rebuild, args.vs_sln, args.no_analysis, args.ida_fcn)
else:
main_no_flutter(
args.indir,
Expand All @@ -401,4 +428,5 @@ def check_for_updates_and_pull():
args.rebuild,
args.vs_sln,
args.no_analysis,
args.ida_fcn,
)
3 changes: 3 additions & 0 deletions blutter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ endif()
if (OLD_MARKING_STACK_BLOCK)
set(defines ${defines} OLD_MARKING_STACK_BLOCK)
endif()
if (IDA_FCN)
set(defines ${defines} IDA_FCN)
endif()
target_compile_definitions(${BINNAME} PRIVATE ${defines})

target_compile_options(${BINNAME} PRIVATE ${cc_opts})
Expand Down
95 changes: 48 additions & 47 deletions blutter/src/DartDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,51 +132,45 @@ void DartDumper::Dump4Radare2(std::filesystem::path outDir)
std::filesystem::create_directory(outDir);
std::ofstream of((outDir / "addNames.r2").string());
of << "# create flags for libraries, classes and methods\n";

// app base & heap base address values changes on every run i.e, setting flag names for them is of no use

// of << fmt::format("f app.base = {:#x}\n", app.base());
// of << fmt::format("f app.heap_base = {:#x}\n", app.heap_base());
of << "e emu.str=true\n";
// app base & heap base address values changes on every run i.e, setting flag names for them is of no use
// but since right now r2 bases it to address 0 let's leave it as it is
// https://github.com/worawit/blutter/pull/104#discussion_r1769637361
of << fmt::format("f app.base = {:#x}\n", app.base());
of << fmt::format("f app.heap_base = {:#x}\n", app.heap_base());

bool show_library = true;
bool show_class = true;
for (auto lib : app.libs) {
std::string lib_prefix = lib->GetName();

std::replace(lib_prefix.begin(), lib_prefix.end(), '$', '_');
std::replace(lib_prefix.begin(), lib_prefix.end(), '&', '_');
std::replace(lib_prefix.begin(), lib_prefix.end(), '-', '_');
std::replace(lib_prefix.begin(), lib_prefix.end(), '+', '_');
filterString(lib_prefix);
for (auto cls : lib->classes) {
std::string cls_prefix = cls->Name();
std::replace(cls_prefix.begin(), cls_prefix.end(), '$', '_');
std::replace(cls_prefix.begin(), cls_prefix.end(), '&', '_');
std::replace(cls_prefix.begin(), cls_prefix.end(), '-', '_');
std::replace(cls_prefix.begin(), cls_prefix.end(), '+', '_');
filterString(cls_prefix);
for (auto dartFn : cls->Functions()) {
const auto ep = dartFn->Address();
auto name = getFunctionName4Ida(*dartFn, cls_prefix);
std::replace(name.begin(), name.end(), '$', '_');
std::replace(name.begin(), name.end(), '&', '_');
std::replace(name.begin(), name.end(), '-', '_');
std::replace(name.begin(), name.end(), '+', '_');
std::replace(name.begin(), name.end(), '?', '_');
std::string name = getFunctionName4Ida(*dartFn, cls_prefix);
filterString(name);
if (show_library) {
of << fmt::format("CC Library({:#x}) = {} @ {}\n", lib->id, lib_prefix, ep);
of << fmt::format("f lib.{}={:#x} # {:#x}\n", lib_prefix, ep, lib->id);
of << fmt::format("'@{:#x}'CC Library({:#x}) = {}\n", ep, lib->id, lib->GetName());
of << fmt::format("'@{:#x}'f lib.{}\n", ep, lib_prefix);
show_library = false;
}
if (show_class) {
of << fmt::format("CC Class({:#x}) = {} @ {}\n", cls->Id(), cls_prefix, ep);
of << fmt::format("f class.{}.{}={:#x} # {:#x}\n", lib_prefix, cls_prefix, ep, cls->Id());
of << fmt::format("'@{:#x}'CC Class({:#x}) = {}\n", ep, cls->Id(), cls->Name());
of << fmt::format("'@{:#x}'f class.{}.{}\n", ep, lib_prefix, cls_prefix);
show_class = false;
}
of << fmt::format("f method.{}.{}.{}_{:x}={:#x}\n", lib_prefix, cls_prefix, name.c_str(), ep, ep);
of << fmt::format("'@{:#x}'f method.{}.{}.{}\n", ep, lib_prefix, cls_prefix, name);
of << fmt::format("'@{:#x}'ic+{}.{}\n", ep, cls_prefix, name);
if (dartFn->HasMorphicCode()) {
of << fmt::format("f method.{}.{}.{}.miss={:#x}\n", lib_prefix, cls_prefix, name.c_str(),
dartFn->PayloadAddress());
of << fmt::format("f method.{}.{}.{}.check={:#x}\n", lib_prefix, cls_prefix, name.c_str(),
dartFn->MonomorphicAddress());
of << fmt::format("'@{:#x}'f method.{}.{}.{}.miss\n",
dartFn->PayloadAddress(),
lib_prefix, cls_prefix, name);
of << fmt::format("'@{:#x}'f method.{}.{}.{}.check\n",
dartFn->MonomorphicAddress(),
lib_prefix, cls_prefix, name);
}
}
show_class = true;
Expand All @@ -187,28 +181,19 @@ void DartDumper::Dump4Radare2(std::filesystem::path outDir)
auto stub = item.second;
const auto ep = stub->Address();
std::string name = stub->FullName();
std::replace(name.begin(), name.end(), '<', '_');
std::replace(name.begin(), name.end(), '>', '_');
std::replace(name.begin(), name.end(), ',', '_');
std::replace(name.begin(), name.end(), ' ', '_');
std::replace(name.begin(), name.end(), '$', '_');
std::replace(name.begin(), name.end(), '&', '_');
std::replace(name.begin(), name.end(), '-', '_');
std::replace(name.begin(), name.end(), '+', '_');
std::replace(name.begin(), name.end(), '?', '_');
std::replace(name.begin(), name.end(), '(', '_'); // https://github.com/AbhiTheModder/blutter-termux/issues/6
std::replace(name.begin(), name.end(), ')', '_');
of << fmt::format("f method.stub.{}_{:x}={:#x}\n", name.c_str(), ep, ep);
std::string flagName = name;
filterString(flagName);
of << fmt::format("'@{:#x}'f method.stub.{}\n", ep, flagName);
}

of << "f pptr=x27\n"; // TODO: hardcoded value
of << "dr x27=`e anal.gp`\n";
of << "'f PP=x27\n";
auto comments = DumpStructHeaderFile((outDir / "r2_dart_struct.h").string());
for (const auto& [offset, comment] : comments) {
if (comment.find("String:") != -1) {
std::string flagFromComment = comment;
filterString(flagFromComment);
of << "f pp." << flagFromComment << "=pptr+" << offset << "\n";
of << "'@0x0+" << offset << "'CC " << comment << "\n";
of << "f pp." << flagFromComment << "=PP+" << offset << "\n";
of << "'@PP+" << offset << "'CC " << comment << "\n";
}
}
}
Expand All @@ -219,6 +204,7 @@ void DartDumper::Dump4Ida(std::filesystem::path outDir)
std::ofstream of((outDir / "addNames.py").string());
of << "import ida_funcs\n";
of << "import idaapi\n\n";
of << "print(\"[+] Adding Function names...\")\n\n";

for (auto lib : app.libs) {
std::string lib_prefix = lib->GetName();
Expand Down Expand Up @@ -250,8 +236,9 @@ void DartDumper::Dump4Ida(std::filesystem::path outDir)
continue;
of << fmt::format("ida_funcs.add_func({:#x}, {:#x})\n", ep, ep + stub->Size());
}
of << "print(\"[+] Done!\")\n";


#ifndef IDA_FCN
// Note: create struct with a lot of member by ida script is very slow
// use header file then adding comment is much faster
auto comments = DumpStructHeaderFile((outDir / "ida_dart_struct.h").string());
Expand All @@ -271,13 +258,27 @@ def create_Dart_structs():
for (const auto& [offset, comment] : comments) {
of << "\tida_struct.set_member_cmt(ida_struct.get_member(struc, " << offset << "), '''" << comment << "''', True)\n";
}
#else
auto comments = DumpStructHeaderFile((outDir / "ida_dart_struct.h").string());
of << R"CBLOCK(
import os
def create_Dart_structs():
sid1 = idc.get_struc_id("DartThread")
if sid1 != idc.BADADDR:
return sid1, idc.get_struc_id("DartObjectPool")
hdr_file = os.path.join(os.path.dirname(__file__), 'ida_dart_struct.h')
idaapi.idc_parse_types(hdr_file, idc.PT_FILE)
sid1 = idc.import_type(-1, "DartThread")
sid2 = idc.import_type(-1, "DartObjectPool")
)CBLOCK";
#endif
of << "\treturn sid1, sid2\n";
of << "thrs, pps = create_Dart_structs()\n";

of << "print('Applying Thread and Object Pool struct')\n";
of << "print('[+] Applying Thread and Object Pool struct')\n";
applyStruct4Ida(of);

of << "print('Script finished!')\n";
of << "print('[+] Script finished!')\n";
}

std::vector<std::pair<intptr_t, std::string>> DartDumper::DumpStructHeaderFile(std::string outFile)
Expand Down
29 changes: 28 additions & 1 deletion dartvm_fetch_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ def load_source(modname, filename):
"""


dart_versions = {
"3.4": ["3.4.0", "3.4.1", "3.4.2", "3.4.3", "3.4.4"],
"3.3": ["3.3.0", "3.3.1", "3.3.2", "3.3.3", "3.3.4"],
"3.5": ["3.5.0", "3.5.1", "3.5.2", "3.5.3"],
"3.0_aa": ["3.0.0", "3.0.1", "3.0.2"],
"3.0_90": ["3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7"],
"3.1": ["3.1.0", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5"],
"3.2": ["3.2.0", "3.2.1", "3.2.2", "3.2.3", "3.2.4", "3.2.5", "3.2.6"],
}


class DartLibInfo:
def __init__(
self,
Expand All @@ -41,7 +52,6 @@ def __init__(
has_compressed_ptrs: bool = None,
snapshot_hash: str = None,
):
self.version = version
self.os_name = os_name
self.arch = arch
self.snapshot_hash = snapshot_hash
Expand All @@ -51,6 +61,23 @@ def __init__(
self.has_compressed_ptrs = os_name != "ios"
else:
self.has_compressed_ptrs = has_compressed_ptrs

if os.path.exists(os.path.join(SCRIPT_DIR, "bin")):
file_name_version = []
suffixes = ["", "_no-analysis", "_ida-fcn", "_no-analysis_ida-fcn", "_no-compressed-ptrs", "_no-compressed-ptrs_no-analysis", "_no-compressed-ptrs_no-analysis_ida-fcn", "_no-compressed-ptrs_ida-fcn"]
for file in os.listdir(os.path.join(SCRIPT_DIR, "bin")):
for suffix in suffixes:
if file.startswith("blutter_dartvm") and file.endswith(f"{os_name}_{arch}{suffix}"):
file_name_version.append(file.split("_")[1].replace('dartvm', ''))

for key, versions in dart_versions.items():
if version in versions and any(v in versions for v in file_name_version):
matched_version = next(v for v in file_name_version if v in versions)
self.lib_name = f"dartvm{matched_version}_{os_name}_{arch}"
self.version = matched_version
return

self.version = version
self.lib_name = f"dartvm{version}_{os_name}_{arch}"


Expand Down