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

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

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

源码:github.com/CYRUS-STUDI...

比如:

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

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

open() 的返回值语义是:

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

  • 失败时:返回 -1

正确的判断方式应该是:

scss 复制代码
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 函数创建目录

ini 复制代码
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

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

结果如下:

scss 复制代码
[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 定义参考:[cs.android.com/android/pla...](https://link.juejin.cn?target=https%3A%2F%2Fcs.android.com%2Fandroid%2Fplatform%2Fsuperproject%2F%2B%2Fandroid15-qpr2-release%3Aexternal%2Fmusl%2Farch%2Fmips64%2Fbits%2Ferrno.h%3Bl%3D13%3Fq%3DEACCES "https://cs.android.com/android/platform/superproject/+/android15-qpr2-release:external/musl/arch/mips64/bits/errno.h;l=13?q=EACCES") 解决方案:把 dump 路径改为:/data/data/\/fart 该目录无需动态申请存储权限,也不受 MANAGE_EXTERNAL_STORAGE 限制。 art_method.cc 中增加以下方法,创建目录并并打印日志 ```c //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 文件存放路径 ```c // 创建目录:/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 的保存路径也要修改 ```c // 保存 CodeItem std::string ins_path = fart_dir + "/" + std::to_string(size_int) + "_ins_" + std::to_string(tid) + ".bin"; ``` 修改完成后重新编译刷机。 ![word/media/image1.png](https://oss.xyyzone.com/jishuzhan/article/1963133759882575873/4be84fb8608dfba91d4b4d70360e7592.webp) 即使 app 没有存储卡读写权限也能正常 dump 了。 ```python 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 中的方法,导致出现下面的日志 ```bash 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 环境 ```scss //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; } ... } ``` ## 完整源码 开源地址:[github.com/CYRUS-STUDI...](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2FCYRUS-STUDIO%2FFART "https://github.com/CYRUS-STUDIO/FART") 相关文章: * [干掉抽取壳!FART 自动化脱壳框架与 Execute 脱壳点解析](https://link.juejin.cn?target=https%3A%2F%2Fcyrus-studio.github.io%2Fblog%2Fposts%2F%25E5%25B9%25B2%25E6%258E%2589%25E6%258A%25BD%25E5%258F%2596%25E5%25A3%25B3fart-%25E8%2587%25AA%25E5%258A%25A8%25E5%258C%2596%25E8%2584%25B1%25E5%25A3%25B3%25E6%25A1%2586%25E6%259E%25B6%25E4%25B8%258E-execute-%25E8%2584%25B1%25E5%25A3%25B3%25E7%2582%25B9%25E8%25A7%25A3%25E6%259E%2590%2F "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://link.juejin.cn?target=https%3A%2F%2Fcyrus-studio.github.io%2Fblog%2Fposts%2Ffart-%25E4%25B8%25BB%25E5%258A%25A8%25E8%25B0%2583%25E7%2594%25A8%25E7%25BB%2584%25E4%25BB%25B6%25E6%25B7%25B1%25E5%25BA%25A6%25E8%25A7%25A3%25E6%259E%2590%25E7%25A0%25B4%25E8%25A7%25A3-art-%25E4%25B8%258B%25E5%2587%25BD%25E6%2595%25B0%25E6%258A%25BD%25E5%258F%2596%25E5%25A3%25B3%25E7%259A%2584%25E7%25BB%2588%25E6%259E%2581%25E6%25AD%25A6%25E5%2599%25A8%2F "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://link.juejin.cn?target=https%3A%2F%2Fcyrus-studio.github.io%2Fblog%2Fposts%2F%25E4%25B8%2580%25E6%25AD%25A5%25E6%25AD%25A5%25E5%25B8%25A6%25E4%25BD%25A0%25E7%25A7%25BB%25E6%25A4%258D-fart-%25E5%2588%25B0-android-10%25E5%25AE%259E%25E7%258E%25B0%25E8%2587%25AA%25E5%258A%25A8%25E5%258C%2596%25E8%2584%25B1%25E5%25A3%25B3%2F "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/")

相关推荐
mooyuan天天6 小时前
DVWA靶场通关笔记-SQL Injection Blind(SQL盲注 Impossible级别)
android·笔记·sql
王喵喵喵6 小时前
每天一个安卓测试开发小知识之 (四)---常用的adb shell命令第二期 pm命令
android·测试
g_i_a_o_giao6 小时前
Android8 从系统启动到用户见到第一个Activity的流程源码分析(三)
android·linux·笔记·学习·安卓framework开发·安卓源码分析
BoomHe7 小时前
Android Studio 内联提示设置
android
paynnne7 小时前
TreeSet原理
android
一条上岸小咸鱼7 小时前
Flutter 类和对象(二):继承
android·kotlin
针叶7 小时前
解决Android Studio查找aar源码的错误
android·gradle·android studio
2501_916008898 小时前
uni-app iOS 应用版本迭代与上架实践 持续更新的高效流程
android·ios·小程序·https·uni-app·iphone·webview
人生游戏牛马NPC1号9 小时前
学习 Android (十八) 学习 OpenCV (三)
android·opencv·学习