FART 自动化脱壳框架一些 bug 修复记录

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

open() 判断不严谨

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); 一直没有执行。

open() 的返回值语义是:

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

  • 失败时:返回 -1

正确的判断方式应该是:

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

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 路径改为:/sdcard/Android/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 文件存放路径 // 创建目录:/sdcard/Android/data//fart std::string base_dir = "/sdcard/Android/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"; 修改完成后重新编译刷机。参考:[移植 FART 到 Android 10 实现自动化脱壳](https://cyrus-studio.github.io/blog/posts/%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/) 即使 app 没有存储卡读写权限也能正常 dump 了。 ![word/media/image1.png](https://i-blog.csdnimg.cn/img_convert/113f557eed207a94db4fd843b06a7503.png) ## 跳过 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 自动化脱壳框架简介与脱壳点的选择](https://cyrus-studio.github.io/blog/posts/fart-%E8%87%AA%E5%8A%A8%E5%8C%96%E8%84%B1%E5%A3%B3%E6%A1%86%E6%9E%B6%E7%AE%80%E4%BB%8B%E4%B8%8E%E8%84%B1%E5%A3%B3%E7%82%B9%E7%9A%84%E9%80%89%E6%8B%A9/) * [FART 主动调用组件设计和源码分析](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%E8%AE%BE%E8%AE%A1%E5%92%8C%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/) * [移植 FART 到 Android 10 实现自动化脱壳](https://cyrus-studio.github.io/blog/posts/%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/)

相关推荐
robotx2 小时前
安卓线程相关
android
消失的旧时光-19432 小时前
Android 面试高频:JSON 文件、大数据存储与断电安全(从原理到工程实践)
android·面试·json
dalancon3 小时前
VSYNC 信号流程分析 (Android 14)
android
dalancon3 小时前
VSYNC 信号完整流程2
android
dalancon3 小时前
SurfaceFlinger 上帧后 releaseBuffer 完整流程分析
android
用户69371750013844 小时前
不卷AI速度,我卷自己的从容——北京程序员手记
android·前端·人工智能
程序员Android5 小时前
Android 刷新一帧流程trace拆解
android
墨狂之逸才5 小时前
解决 Android/Gradle 编译报错:Comparison method violates its general contract!
android
阿明的小蝴蝶6 小时前
记一次Gradle环境的编译问题与解决
android·前端·gradle
汪海游龙6 小时前
开源项目 Trending AI 招募 Google Play 内测人员(12 名)
android·github