FART 自动化脱壳框架优化实战:Bug 修复与代码改进记录

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/

open() 判断不严谨导致 dex 重复 dump

源码:https://github.com/CYRUS-STUDIO/FART/blob/master/fart10/art/runtime/art_method.cc

比如:

复制代码
int dexfilefp = open(dex_path.c_str(), O_RDONLY);
if (dexfilefp > 0) {
    close(dexfilefp);
}

这个判断条件其实是不严谨的,导致 if 中 的 close(dexfilefp); 一直没有执行,dex 会重复 dump。

open() 的返回值语义是:

  • 成功时:返回一个非负整数(即 >= 0),它是打开的文件描述符。

  • 失败时:返回 -1

正确的判断方式应该是:

复制代码
if (fp >= 0) {
    // 成功打开
} else {
    // 打开失败,打印错误信息
    LOG(ERROR) << "open dex file failed";
}

减少不必要的 I/O,提高 dump 成功率。

dump 目录创建失败(mkdir failed errno: 13)

FART 中通过 mkdir 函数在 sdcard 上创建 dump 文件存放目录,但是这样必须 app 拥有存储卡读写权限。不然 mkdir 会执行失败。

下面是一个 frida 脚本,调用系统 的 mkdir 函数创建目录

复制代码
function mkdir(path) {
    const libc = Module.findExportByName(null, "mkdir");
    if (!libc) {
        console.error("[-] Cannot find mkdir symbol.");
        return;
    }

    const mkdirNative = new NativeFunction(libc, 'int', ['pointer', 'int']);

    const pathStr = Memory.allocUtf8String(path);
    const mode = 0o777; // 权限

    const result = mkdirNative(pathStr, mode);
    if (result === 0) {
        console.log("[+] mkdir success:", path);
    } else {
        const errnoLocation = Module.findExportByName(null, "__errno");
        if (errnoLocation) {
            const errnoPtr = new NativeFunction(errnoLocation, 'pointer', []);
            const errnoValue = Memory.readU32(errnoPtr());
            console.error("[-] mkdir failed errno:", errnoValue);
        } else {
            console.error("[-] mkdir failed");
        }
    }
}

调用 mkdir 创建目录 /sdcard/fart/com.cyrus.example

复制代码
mkdir("/sdcard/fart/com.cyrus.example");

结果如下:

复制代码
[Remote::com.cyrus.example]-> mkdir("/sdcard/fart/com.cyrus.example");
[-] mkdir failed errno: 13

-\] mkdir failed errno: 13 表示 Permission denied(权限被拒绝)。 **常见的 errno 错误码:** | errno 值 | 含义 | |--------------|---------------| | EEXIST | 目录已经存在 | | EACCES | 权限不足 | | ENOENT | 上级目录不存在 | | ENAMETOOLONG | 路径太长 | | ENOSPC | 没有磁盘空间或 inode | errno 定义参考: 解决方案:把 dump 路径改为:/data/data/\/fart 该目录无需动态申请存储权限,也不受 MANAGE_EXTERNAL_STORAGE 限制。 art_method.cc 中增加以下方法,创建目录并并打印日志 //add 创建目录 bool ensure_dir_exists(const std::string& path) { int res = mkdir(path.c_str(), 0777); if (res == 0 || errno == EEXIST) { return true; } else { LOG(ERROR) << "mkdir failed: " << path << ", errno=" << errno << ", " << errno; return false; } } 修改 dumpDexFileByExecute 和 dumpArtMethod 方法中 dump 文件存放路径 // 创建目录:/data/data//fart std::string base_dir = "/data/data/"; std::string app_dir = base_dir + szProcName; std::string fart_dir = app_dir + "/fart"; ensure_dir_exists(app_dir); ensure_dir_exists(fart_dir); // 保存 dex 文件 std::string dex_path = fart_dir + "/" + std::to_string(size_int) + "_dex_file.dex"; // 保存 class 列表 std::string class_list_path = fart_dir + "/" + std::to_string(size_int) + "_class_list.txt"; dumpArtMethod 方法中 CodeItem 的保存路径也要修改 // 保存 CodeItem std::string ins_path = fart_dir + "/" + std::to_string(size_int) + "_ins_" + std::to_string(tid) + ".bin"; 修改完成后重新编译刷机。 ![word/media/image1.png](https://i-blog.csdnimg.cn/img_convert/7ba8093cbd679b2632df303c54dd6d40.png) 即使 app 没有存储卡读写权限也能正常 dump 了。 wayne:/data/data/com.cyrus.example/fart # ls 1321896_class_list.txt 1437648_dex_file_execute.dex 1488168_class_list_execute.txt 1605504_ins_4714.bin 1321896_class_list_execute.txt 1437648_ins_4714.bin 1488168_dex_file.dex 198768_class_list.txt 1321896_dex_file.dex 1448488_class_list.txt 1488168_dex_file_execute.dex 198768_class_list_execute.txt 1321896_dex_file_execute.dex 1448488_class_list_execute.txt 1488168_ins_4714.bin 198768_dex_file.dex 1321896_ins_4714.bin 1448488_dex_file.dex 1496608_class_list.txt 198768_dex_file_execute.dex 1351008_class_list.txt 1448488_dex_file_execute.dex 1496608_class_list_execute.txt 198768_ins_4714.bin 1351008_class_list_execute.txt 1448488_ins_4714.bin 1496608_dex_file.dex 3782924_class_list_execute.txt 1351008_dex_file.dex 1461504_class_list.txt 1496608_dex_file_execute.dex 3782924_dex_file_execute.dex 1351008_dex_file_execute.dex 1461504_class_list_execute.txt 1496608_ins_4714.bin 400440_class_list_execute.txt 1351008_ins_4714.bin 1461504_dex_file.dex 1537456_class_list.txt 400440_dex_file_execute.dex 1403328_class_list.txt 1461504_dex_file_execute.dex 1537456_class_list_execute.txt 4376620_class_list_execute.txt 1403328_class_list_execute.txt 1461504_ins_4714.bin 1537456_dex_file.dex 4376620_dex_file_execute.dex 1403328_dex_file.dex 1472352_class_list.txt 1537456_dex_file_execute.dex 590624_class_list.txt 1403328_dex_file_execute.dex 1472352_class_list_execute.txt 1537456_ins_4714.bin 590624_class_list_execute.txt 1403328_ins_4714.bin 1472352_dex_file.dex 1571616_class_list.txt 590624_dex_file.dex 1423432_class_list.txt 1472352_dex_file_execute.dex 1571616_class_list_execute.txt 590624_dex_file_execute.dex 1423432_class_list_execute.txt 1472352_ins_4714.bin 1571616_dex_file.dex 590624_ins_4714.bin 1423432_dex_file.dex 1481472_class_list.txt 1571616_dex_file_execute.dex 7387912_class_list_execute.txt 1423432_dex_file_execute.dex 1481472_class_list_execute.txt 1571616_ins_4714.bin 7387912_dex_file_execute.dex 1423432_ins_4714.bin 1481472_dex_file.dex 1605504_class_list.txt 8391596_class_list_execute.txt 1437648_class_list.txt 1481472_dex_file_execute.dex 1605504_class_list_execute.txt 8391596_dex_file_execute.dex 1437648_class_list_execute.txt 1481472_ins_4714.bin 1605504_dex_file.dex 9085048_class_list_execute.txt 1437648_dex_file.dex 1488168_class_list.txt 1605504_dex_file_execute.dex 9085048_dex_file_execute.dex ## Android 编译构建阶段的 dump 调用干扰 在 Android 编译构建阶段的 dex2oatd 工具执行时 调用了 art_method.cc 中的方法,导致出现下面的日志 dex2oatd E 05-27 19:26:24 7330 7330 art_method.cc:151] mkdir failed: /sdcard/Android/data/out/soong/host/linux-x86/bin/dex2oatd, errno=2, 2 dex2oatd E 05-27 19:26:24 7330 7330 art_method.cc:151] mkdir failed: /sdcard/Android/data/out/soong/host/linux-x86/bin/dex2oatd/fart, errno=2, 2 dex2oatd E 05-27 19:26:24 7330 7330 art_method.cc:234] [dumpDexFileByExecute] /sdcard/Android/data/out/soong/host/linux-x86/bin/dex2oatd/fart/4376620_dex_file_execute.dex open failed, fp=-1 dex2oatd E 05-27 19:26:24 7330 7330 art_method.cc:151] mkdir failed: /sdcard/Android/data/out/soong/host/linux-x86/bin/dex2oatd, errno=2, 2 dex2oatd E 05-27 19:26:24 7330 7330 art_method.cc:151] mkdir failed: /sdcard/Android/data/out/soong/host/linux-x86/bin/dex2oatd/fart, errno=2, 2 在 dump 前加上判断是否运行在 Android 环境 //add 跳过 Android 编译构建阶段的 dex2oatd 工具执行时的调用 bool isValidAndroidApp(const char* procName) { // 排除 host 工具,例如 dex2oat、dex2oatd、aapt2 等 return procName != nullptr && strstr(procName, "/") == nullptr && // 不应该包含路径 strstr(procName, "dex2oat") == nullptr && // 排除 dex2oat/dex2oatd strstr(procName, "soong") == nullptr; // 排除构建系统相关路径 } //add extern "C" void dumpDexFileByExecute(ArtMethod* artmethod) REQUIRES_SHARED(Locks::mutator_lock_) { ... if (szProcName[0] == '\0') { LOG(WARNING) << "[dumpDexFileByExecute] 获取进程名失败:" << artmethod->PrettyMethod(); return; } if (!isValidAndroidApp(szProcName)) { LOG(WARNING) << "[dumpDexFileByExecute] 当前进程 " << szProcName << " 非法,跳过 dex dump"; return; } ... } //add extern "C" void dumpArtMethod(ArtMethod* artmethod) REQUIRES_SHARED(Locks::mutator_lock_) { ... if (szProcName[0] == '\0') { LOG(WARNING) << "[dumpArtMethod] 获取进程名失败:" << artmethod->PrettyMethod(); return; } if (!isValidAndroidApp(szProcName)) { LOG(WARNING) << "[dumpArtMethod] 当前进程 " << szProcName << " 非法,跳过 dex dump"; return; } ... } ## 完整源码 开源地址: 相关文章: * [干掉抽取壳!FART 自动化脱壳框架与 Execute 脱壳点解析](https://cyrus-studio.github.io/blog/posts/%E5%B9%B2%E6%8E%89%E6%8A%BD%E5%8F%96%E5%A3%B3fart-%E8%87%AA%E5%8A%A8%E5%8C%96%E8%84%B1%E5%A3%B3%E6%A1%86%E6%9E%B6%E4%B8%8E-execute-%E8%84%B1%E5%A3%B3%E7%82%B9%E8%A7%A3%E6%9E%90/) * [FART 主动调用组件深度解析:破解 ART 下函数抽取壳的终极武器](https://cyrus-studio.github.io/blog/posts/fart-%E4%B8%BB%E5%8A%A8%E8%B0%83%E7%94%A8%E7%BB%84%E4%BB%B6%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90%E7%A0%B4%E8%A7%A3-art-%E4%B8%8B%E5%87%BD%E6%95%B0%E6%8A%BD%E5%8F%96%E5%A3%B3%E7%9A%84%E7%BB%88%E6%9E%81%E6%AD%A6%E5%99%A8/) * [一步步带你移植 FART 到 Android 10,实现自动化脱壳](https://cyrus-studio.github.io/blog/posts/%E4%B8%80%E6%AD%A5%E6%AD%A5%E5%B8%A6%E4%BD%A0%E7%A7%BB%E6%A4%8D-fart-%E5%88%B0-android-10%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%8A%A8%E5%8C%96%E8%84%B1%E5%A3%B3/)

相关推荐
liang_jy17 小时前
Android SparseArray
android·源码
2501_9272835817 小时前
荣联汇智助力天津艺虹打造“软硬一体”智慧工厂,全流程自动化引领印刷包装行业数智变革
大数据·运维·数据仓库·人工智能·低代码·自动化
liang_jy18 小时前
Activity 启动流程扩展篇(一)—— startActivityInner 任务决策全解析
android·源码
HXDGCL18 小时前
矩形环形导轨:自动化循环线的核心运动单元解析
运维·算法·自动化
NPE~19 小时前
[App逆向]脱壳实战
android·教程·逆向·android逆向·逆向分析
木易 士心19 小时前
别再只会用 drawCircle 了!一文搞懂 Android Canvas 底层机制
android
掌心向暖RPA自动化21 小时前
如何获取网页某个元素在屏幕可见部分的中心坐标影刀RPA懒加载坐标定位技巧
java·javascript·自动化·rpa·影刀rpa
AtOR CUES21 小时前
MySQL——表操作及查询
android·mysql·adb
怣疯knight1 天前
安卓App无法增加自定义图片作为图标功能
android
架构源启1 天前
OpenClaw 只能手动写脚本?我用 Chrome 插件实现了“录制即生成“
前端·人工智能·chrome·自动化