当前位置: 首页 > news >正文

攻防 FART 脱壳:特征检测识别 + 对抗绕过全解析

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

FART 对抗

某视频 app 的壳在启动的时候会检测 FART 特征,日志输出如下:

2025-05-29 02:16:25.612  2557-2557  ActivityThread          cn.cntv                              E  go into handleBindApplication
2025-05-29 02:16:25.630  2557-2557  cn.cntv                 cn.cntv                              I  The ClassLoaderContext is a special shared library.
2025-05-29 02:16:25.807  1512-17245 ActivityManager         system_process                       I  Process cn.cntv (pid 2557) has died: fore TOP 
2025-05-29 02:16:25.875  1512-1588  ActivityManager         system_process                       I  Start proc 2628:cn.cntv/u0a140 for top-activity {cn.cntv/com.cctv.mcctv.ui.activity.SplashActivity}
2025-05-29 02:16:25.932  2628-2628  ActivityThread          cn.cntv                              E  go into handleBindApplication
2025-05-29 02:16:25.945  2628-2628  cn.cntv                 cn.cntv                              I  The ClassLoaderContext is a special shared library.
2025-05-29 02:16:26.113  1512-4110  ActivityManager         system_process                       I  Process cn.cntv (pid 2628) has died: fore TOP 
2025-05-29 02:16:26.179  1512-1588  ActivityManager         system_process                       I  Start proc 2716:cn.cntv/u0a140 for top-activity {cn.cntv/com.cctv.mcctv.ui.activity.SplashActivity}
2025-05-29 02:16:26.233  2716-2716  ActivityThread          cn.cntv                              E  go into handleBindApplication
2025-05-29 02:16:26.245  2716-2716  cn.cntv                 cn.cntv                              I  The ClassLoaderContext is a special shared library.
2025-05-29 02:16:26.291  2716-2716  cn.cntv                 cn.cntv                              W  type=1400 audit(0.0:126069): avc: granted { execute } for path="/data/data/cn.cntv/files/libexec.so" dev="mmcblk0p64" ino=157243 scontext=u:r:untrusted_app:s0:c140,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c140,c256,c512,c768 tclass=file app=cn.cntv
2025-05-29 02:16:26.304  2716-2716  cn.cntv                 cn.cntv                              W  type=1400 audit(0.0:126070): avc: granted { execute } for path="/data/data/cn.cntv/files/libexecmain.so" dev="mmcblk0p64" ino=157244 scontext=u:r:untrusted_app:s0:c140,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c140,c256,c512,c768 tclass=file app=cn.cntv
2025-05-29 02:16:26.324  2716-2716  cn.cntv                 cn.cntv                              W  type=1400 audit(0.0:126071): avc: denied { execmod } for path="/apex/com.android.runtime/lib64/libart.so" dev="mmcblk0p61" ino=313 scontext=u:r:untrusted_app:s0:c140,c256,c512,c768 tcontext=u:object_r:system_lib_file:s0 tclass=file permissive=0 app=cn.cntv
2025-05-29 02:16:26.334  2716-2716  cn.cntv                 cn.cntv                              W  type=1400 audit(0.0:126072): avc: denied { execmod } for path="/system/lib64/liblog.so" dev="mmcblk0p61" ino=3229 scontext=u:r:untrusted_app:s0:c140,c256,c512,c768 tcontext=u:object_r:system_lib_file:s0 tclass=file permissive=0 app=cn.cntv
2025-05-29 02:16:26.385  1512-17245 ActivityManager         system_process                       I  Process cn.cntv (pid 2716) has died: fore TOP 
2025-05-29 02:16:26.441  1512-1588  ActivityManager         system_process                       I  Start proc 2807:cn.cntv/u0a140 for top-activity {cn.cntv/com.cctv.mcctv.ui.activity.SplashActivity}
2025-05-29 02:16:26.491  2807-2807  ActivityThread          cn.cntv                              E  go into handleBindApplication
2025-05-29 02:16:26.506  2807-2807  cn.cntv                 cn.cntv                              I  The ClassLoaderContext is a special shared library.
2025-05-29 02:16:26.682  1512-17245 ActivityManager         system_process                       I  Process cn.cntv (pid 2807) has died: fore TOP 
2025-05-29 02:16:26.731  1512-1588  ActivityManager         system_process                       I  Start proc 2872:cn.cntv/u0a140 for top-activity {cn.cntv/com.cctv.mcctv.ui.activity.SplashActivity}
2025-05-29 02:16:26.783  2872-2872  ActivityThread          cn.cntv                              E  go into handleBindApplication

使用的是 ajm 的壳,App 加载 so 文件,主动检测 FART 特征

avc: granted { execute } for path="/data/data/cn.cntv/files/libexec.so"
avc: granted { execute } for path="/data/data/cn.cntv/files/libexecmain.so"

一旦发现异常就触发崩溃(kill)

Process cn.cntv (pid 2628) has died: fore TOP 

如何实现类似的功能?

  1. 首先找到 FART 的特征

  2. FART 特征检测识别

  3. 识别到 FART 特征 kill 进程,没有识别到正常进入 app

FART特征

FART 有什么特征?通过查看 FART 源码可以找到。

FART 开源地址:https://github.com/CYRUS-STUDIO/FART

关于 FART 的详细介绍参考下面的文章:

  • FART 自动化脱壳框架简介与脱壳点的选择

  • FART 主动调用组件设计和源码分析

  • 移植 FART 到 Android 10 实现自动化脱壳

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

  • 使用 Frida 增强 FART:实现更强大的 Android 脱壳能力

ActivityThread

源码:https://github.com/CYRUS-STUDIO/FART/blob/master/fart10/frameworks/base/core/java/android/app/ActivityThread.java

FART 在 ActivityThread 新增了以下方法,这些都可以作为 FART 的特征

public static Field getClassField(ClassLoader classloader, String class_name, String filedName)
public static Object getClassFieldObject(ClassLoader classloader, String class_name, Object obj, String filedName)
public static Object invokeStaticMethod(String class_name, String method_name, Class[] pareTyple, Object[] pareVaules)
public static Object getFieldOjbect(String class_name, Object obj, String filedName)
public static ClassLoader getClassloader()
public static void loadClassAndInvoke(ClassLoader appClassloader, String eachclassname, Method dumpMethodCode_method) 
public static void fart() 
public static void fartwithClassloader(ClassLoader appClassloader)
public static void fartthread()

DexFile

源码:https://github.com/CYRUS-STUDIO/FART/blob/master/fart10/libcore/dalvik/src/main/java/dalvik/system/DexFile.java

FART 在 DexFile 新增了 dumpMethodCode 方法同样也可以作为 FART 的特征

private static native void dumpMethodCode(Object m);

art_method.cc

FART 在 art/runtime/art_method.cc 中新增以下方法

uint8_t* codeitem_end(const uint8_t **pData)
extern "C" char *base64_encode(char *str,long str_len,long* outlen)
extern "C" void dumpDexFileByExecute(ArtMethod* artmethod)
extern "C" void dumpArtMethod(ArtMethod* artmethod)
extern "C" void myfartInvoke(ArtMethod* artmethod)

dalvik_system_DexFile.cc

FART 在 art/runtime/native/dalvik_system_DexFile.cc 中新增了以下方法

static void DexFile_dumpMethodCode(JNIEnv* env, jclass,jobject method)

java_lang_reflect_Method.cc

FART 在 art/runtime/native/java_lang_reflect_Method.cc 中新增了以下方法

extern "C" ArtMethod* jobject2ArtMethod(JNIEnv* env, jobject javaMethod)

上面这些都可以作为 FART 特征。

FART 特征有的在 native 层,最终编译成 so 文件;有的在 java 层,最终编译成 dex 相关文件。

如何找到这些 so 和 dex 相关文件?

/proc/self/maps

/proc/self/maps 是 Linux(含 Android)系统中一个非常重要的伪文件,它提供了当前进程内存映射(memory mapping)信息,是分析当前进程加载了哪些资源的重要窗口。

包括:

  • 加载的 .so 动态库

  • 加载的 .dex 文件(包含 ODEX / VDEX)

  • 映射的 Java 堆、native 堆、stack 等

  • 匿名 mmap 内存区域

  • JIT 编译生成的代码段

  • 映射的 /system/, /data/, /apex/, /dev/ashmem 等文件

比如,进入 adb shell ,通过下面命令读取包名 com.cyrus.example 下的 maps 文件

cat /proc/$(pidof com.cyrus.example)/maps

输出结果如下:

12c00000-12c80000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
12c80000-132c0000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
132c0000-13580000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
13580000-26280000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
26280000-2a940000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
2a940000-2a980000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
2a980000-2a9c0000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
2a9c0000-2aac0000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
2aac0000-2ab80000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
2ab80000-2abc0000 ---p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
2abc0000-2ac00000 rw-p 00000000 00:00 0                                  [anon:dalvik-main space (region space)]
708d5000-70b5a000 rw-p 00000000 103:1d 1863                              /system/framework/arm64/boot.art
70b5a000-70c4a000 rw-p 00000000 103:1d 1833                              /system/framework/arm64/boot-core-libart.art
70c4a000-70c80000 rw-p 00000000 103:1d 1848                              /system/framework/arm64/boot-okhttp.art
70c80000-70cc1000 rw-p 00000000 103:1d 1830                              /system/framework/arm64/boot-bouncycastle.art
70cc1000-70cd1000 rw-p 00000000 103:1d 1827                              /system/framework/arm64/boot-apache-xml.art
70cd1000-71595000 rw-p 00000000 103:1d 1839                              /system/framework/arm64/boot-framework.art
71595000-715c9000 rw-p 00000000 103:1d 1836                              /system/framework/arm64/boot-ext.art
715c9000-716c1000 rw-p 00000000 103:1d 1854                              /system/framework/arm64/boot-telephony-common.art
716c1000-716cf000 rw-p 00000000 103:1d 1860                              /system/framework/arm64/boot-voip-common.art
716cf000-716e4000 rw-p 00000000 103:1d 1842                              /system/framework/arm64/boot-ims-common.art
716e4000-716e7000 rw-p 00000000 103:1d 1824                              /system/framework/arm64/boot-android.test.base.art
716e7000-716e9000 rw-p 00000000 103:1d 1851                              /system/framework/arm64/boot-org.ifaa.android.manager.art
716e9000-716f0000 rw-p 00000000 103:1d 1845                              /system/framework/arm64/boot-ims-ext-common_system.art
716f0000-716f4000 rw-p 00000000 103:1d 1857                              /system/framework/arm64/boot-telephony-ext.art
716f4000-716fc000 rw-p 00000000 103:1d 1821                              /system/framework/arm64/boot-WfdCommon.art
716fc000-717b2000 r--p 00000000 103:1d 1864                              /system/framework/arm64/boot.oat
717b2000-71a4d000 r-xp 000b6000 103:1d 1864                              /system/framework/arm64/boot.oat
71a4d000-71a4e000 rw-p 00000000 00:00 0                                  [anon:.bss]
71a4e000-71a50000 r--s 00000000 103:1d 1882                              /system/framework/boot.vdex
71a50000-71a51000 r--p 00351000 103:1d 1864                              /system/framework/arm64/boot.oat
71a51000-71a52000 rw-p 00352000 103:1d 1864                              /system/framework/arm64/boot.oat
71a52000-71a9b000 r--p 00000000 103:1d 1834                              /system/framework/arm64/boot-core-libart.oat
71a9b000-71ba3000 r-xp 00049000 103:1d 1834                              /system/framework/arm64/boot-core-libart.oat
...

比如:

71a9b000-71ba3000 r-xp 00049000 103:1d 1834  /system/framework/arm64/boot-core-libart.oat

字段解析如下:

字段示例值含义
71a9b000-71ba3000起始地址 - 结束地址表示这段内存从 0x71a9b000 映射到 0x71ba3000(大约 1MB)
r-xp权限r = 可读,x = 可执行,p = 私有
00049000文件偏移映射文件时从 offset=0x49000 开始
103:1d设备编号表示该文件所在设备的主/次设备号
1834inode 号文件在设备上的 inode 编号
/system/framework/arm64/boot-core-libart.oat文件路径表示映射的文件路径,来源是系统的 OAT 文件

所有我们可以通过读取 /proc/self/maps 得到当前 app 加载的所有资源文件,实现如下:

extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_cyrus_example_fart_AntiFART_listLoadedFiles(JNIEnv *env, jclass) {std::ifstream maps("/proc/self/maps");std::string line;std::vector<std::string> paths;while (std::getline(maps, line)) {std::size_t pathPos = line.find('/');if (pathPos != std::string::npos) {std::string path = line.substr(pathPos);if (std::find(paths.begin(), paths.end(), path) == paths.end()) {paths.push_back(path);}}}jclass stringClass = env->FindClass("java/lang/String");jobjectArray result = env->NewObjectArray(paths.size(), stringClass, nullptr);for (size_t i = 0; i < paths.size(); ++i) {env->SetObjectArrayElement(result, i, env->NewStringUTF(paths[i].c_str()));}return result;
}

效果如下:

word/media/image1.png

so 文件 FART 特征检测

对于 FART 在 C/C++ 层添加的函数特征码检测。

通过检测 /proc/self/maps下的加载 so库列表得到各个库文件绝对路径

// 读取 /proc/self/maps 获取加载的 .so 路径
std::set<std::string> get_loaded_so_paths() {std::set<std::string> so_paths;std::ifstream maps("/proc/self/maps");std::string line;std::regex so_regex(".+\\.so(\\s|$)");while (std::getline(maps, line)) {std::size_t path_pos = line.find('/');if (path_pos != std::string::npos) {std::string path = line.substr(path_pos);if (std::regex_search(path, so_regex)) {so_paths.insert(path);}}}return so_paths;
}

再通过 fopen 函数将 so 库的内容以16进制读进来放在内存,采用字符串模糊查找来检测是否命中黑名单中的方法特征码。

// so 黑名单函数特征
std::vector<std::string> so_symbols_blacklist = {"dumpDexFileByExecute","dumpArtMethod","myfartInvoke","DexFile_dumpMethodCode"
};// 读取文件内容为字符串
std::string read_file_content(const std::string &path) {FILE *file = fopen(path.c_str(), "rb");if (!file) {LOGI("Failed to open: %s", path.c_str());return "";}fseek(file, 0, SEEK_END);long size = ftell(file);rewind(file);std::string buffer(size, 0);fread(&buffer[0], 1, size, file);fclose(file);return buffer;
}// 单词边界检查
bool is_word_boundary(char ch) {return !std::isalnum(static_cast<unsigned char>(ch)) && ch != '_';
}// 返回匹配到的特征列表
std::vector<std::string> get_matched_signatures(const std::string &content, const std::vector<std::string> &patterns) {std::vector<std::string> matched;for (const auto &pattern : patterns) {size_t pos = content.find(pattern);if (pos != std::string::npos) {// 类似 DexFile_dumpMethodCode 这种,带 _ 的不需要做单词边界检查if (pattern.find('_') != std::string::npos) {matched.push_back(pattern);}else{// 单词边界检查// 这样就不会匹配 farther、himmelfart,但可以匹配像 void fart()、"fart"、 call fart 等形式。char prev = (pos == 0) ? '\0' : content[pos - 1];char next = (pos + pattern.length() < content.size()) ? content[pos + pattern.length()] : '\0';if (is_word_boundary(prev) && is_word_boundary(next)) {matched.push_back(pattern);}}}}return matched;
}// JNI 方法:检测已加载 .so 中是否包含黑名单符号
extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_cyrus_example_fart_AntiFART_detectFartInLoadedSO(JNIEnv *env, jclass clazz) {std::vector<std::string> detected_logs;auto so_paths = get_loaded_so_paths();for (const auto &path: so_paths) {std::string content = read_file_content(path);if (!content.empty()) {std::vector<std::string> matched = get_matched_signatures(content, so_symbols_blacklist);if (!matched.empty()) {std::ostringstream oss;oss << "[FART DETECTED] " << path << " => ";for (size_t i = 0; i < matched.size(); ++i) {oss << matched[i];if (i != matched.size() - 1) oss << ", ";}LOGI("%s", oss.str().c_str());detected_logs.push_back(oss.str());}}}jclass stringClass = env->FindClass("java/lang/String");jobjectArray result = env->NewObjectArray(detected_logs.size(), stringClass, nullptr);for (int i = 0; i < detected_logs.size(); ++i) {env->SetObjectArrayElement(result, i, env->NewStringUTF(detected_logs[i].c_str()));}return result;
}

可以看到在 libart.so 中命中了多个 FART 特征。

word/media/image2.png

dex 文件 FART 特征检测

对于 FART 在 Java 层添加的方法特征码检测也是类似。

但是 dex 相关文件格式有多种,包括:

  • .dex 文件(原始 dex)

  • .odex(优化过的 dex)

  • .vdex(Verified DEX)

  • .art(预编译的 ART 文件)

  • 以及 .jar、.apk 中可能包含 dex 文件的路径

读取 /proc/self/maps 获取加载的 dex 或 dex 相关文件路径

// 读取 /proc/self/maps 获取加载的 dex 或 dex 相关文件路径
std::set<std::string> get_loaded_dex_paths() {std::set<std::string> dex_paths;std::ifstream maps("/proc/self/maps");std::string line;// 匹配 dex、odex、vdex、art、apk、jar 文件std::regex dex_regex(R"((\.dex|\.odex|\.vdex|\.art|\.apk|\.jar)(\s|$))");while (std::getline(maps, line)) {std::size_t path_pos = line.find('/');if (path_pos != std::string::npos) {std::string path = line.substr(path_pos);if (std::regex_search(path, dex_regex)) {dex_paths.insert(path);}}}return dex_paths;
}

再通过 fopen 函数将 dex 相关文件的内容以16进制读进来放在内存,采用字符串模糊查找来检测是否命中黑名单中的方法特征码。

// dex 黑名单函数特征
const std::vector<std::string> dex_method_blacklist = {"loadClassAndInvoke","fart","fartwithClassloader","fartthread"
};// JNI 方法:检测已加载 dex 中是否包含黑名单符号
extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_cyrus_example_fart_AntiFART_detectFartInLoadedDex(JNIEnv *env, jclass clazz) {std::vector<std::string> detected_logs;auto dex_paths = get_loaded_dex_paths();for (const auto &path: dex_paths) {std::string content = read_file_content(path);if (!content.empty()) {std::vector<std::string> matched = get_matched_signatures(content, dex_method_blacklist);if (!matched.empty()) {std::ostringstream oss;oss << "[FART DETECTED] " << path << " => ";for (size_t i = 0; i < matched.size(); ++i) {oss << matched[i];if (i != matched.size() - 1) oss << ", ";}LOGI("%s", oss.str().c_str());detected_logs.push_back(oss.str());}}}jclass stringClass = env->FindClass("java/lang/String");jobjectArray result = env->NewObjectArray(detected_logs.size(), stringClass, nullptr);for (int i = 0; i < detected_logs.size(); ++i) {env->SetObjectArrayElement(result, i, env->NewStringUTF(detected_logs[i].c_str()));}return result;
}

可以看到在 framework.jar 中检测到了多个 FART 特征。

word/media/image3.png

反 FART 对抗

绕过 FART 对抗只需要定制个性化的 ROM,抹除这些 FART 特征就好了。

抹除 FART 特征

比如把这些 FART 中默认的方法名重命名一下就好了。

public static Field getClassField(ClassLoader classloader, String class_name, String filedName)
public static Object getClassFieldObject(ClassLoader classloader, String class_name, Object obj, String filedName)
public static Object invokeStaticMethod(String class_name, String method_name, Class[] pareTyple, Object[] pareVaules)
public static Object getFieldOjbect(String class_name, Object obj, String filedName)
public static ClassLoader getClassloader()
public static void loadClassAndInvoke(ClassLoader appClassloader, String eachclassname, Method dumpMethodCode_method) 
public static void fart() 
public static void fartwithClassloader(ClassLoader appClassloader)
public static void fartthread()
private static native void dumpMethodCode(Object m);uint8_t* codeitem_end(const uint8_t **pData)
extern "C" char *base64_encode(char *str,long str_len,long* outlen)
extern "C" void dumpDexFileByExecute(ArtMethod* artmethod)
extern "C" void dumpArtMethod(ArtMethod* artmethod)
extern "C" void myfartInvoke(ArtMethod* artmethod)static void DexFile_dumpMethodCode(JNIEnv* env, jclass,jobject method)extern "C" ArtMethod* jobject2ArtMethod(JNIEnv* env, jobject javaMethod)

假设把函数重命名如下:

Java 层重命名:

原方法名替代方法名
getClassFieldresolveDeclaredField
getClassFieldObjectextractFieldValue
invokeStaticMethodinvokeStaticByName
getFieldOjbectgetInstanceFieldValue
getClassloaderobtainAppClassLoader
loadClassAndInvokedispatchClassTask
fartstartCodeInspection
fartwithClassloaderstartCodeInspectionWithCL
fartthreadlaunchInspectorThread
dumpMethodCodenativeDumpCode

Native 层函数重命名:

原函数名替代函数名
codeitem_endgetDexCodeItemEnd
base64_encodeencodeBase64Buffer
dumpDexFileByExecutetraceDexExecution
dumpArtMethodtraceMethodCode
myfartInvokecallNativeMethodInspector
DexFile_dumpMethodCodeDexFile_nativeDumpCode
jobject2ArtMethodconvertToArtMethodPtr

记得相关函数调用也要做修改。

自动化脚本

但是一个个修改太麻烦了,写个脚本自动修改(可以自定义 RENAME_MAP 中的值去定制一个只属于自己的 FART ROM,这样就不容易被检测):

import os
import re# 敏感方法名及其替代名映射表
RENAME_MAP = {"getClassField": "resolveDeclaredField","getClassFieldObject": "extractFieldValue","invokeStaticMethod": "invokeStaticByName","getFieldOjbect": "getInstanceFieldValue","getClassloader": "obtainAppClassLoader","loadClassAndInvoke": "dispatchClassTask","fart\\b": "startCodeInspection","fartwithClassloader": "startCodeInspectionWithCL","fartthread": "launchInspectorThread","dumpMethodCode": "nativeDumpCode","codeitem_end": "getDexCodeItemEnd","base64_encode": "encodeBase64Buffer","dumpDexFileByExecute": "traceDexExecution","dumpArtMethod": "traceMethodCode","myfartInvoke": "callNativeMethodInspector","DexFile_dumpMethodCode": "DexFile_nativeDumpCode","jobject2ArtMethod": "convertToArtMethodPtr"
}SOURCE_SUFFIX = (".java", ".kt", ".cc", ".c", ".cpp", ".h", ".js")def replace_in_file(file_path):try:with open(file_path, "r", encoding="utf-8") as f:content = f.read()original_content = contentfor old, new in RENAME_MAP.items():content = re.sub(r'\b' + old + r'\b', new, content)if content != original_content:with open(file_path, "w", encoding="utf-8") as f:f.write(content)print(f"[UPDATED] {file_path}")else:print(f"[SKIPPED] {file_path}")except Exception as e:print(f"[ERROR] {file_path}: {e}")def scan_directory(root_dir):for dirpath, _, filenames in os.walk(root_dir):for file in filenames:if file.endswith(SOURCE_SUFFIX):replace_in_file(os.path.join(dirpath, file))if __name__ == "__main__":import sysif len(sys.argv) < 2:print("Usage: python rename_fart_symbols.py <source_directory>")sys.exit(1)scan_directory(sys.argv[1])input("Press Enter to exit...")

执行脚本:

D:\Projects\FART\rename_fart_symbols.py D:\Projects\FART\fart10

替换完成。

word/media/image4.png

同时也可以用来修改 frida_fart 的 js 源码

D:\Projects\FART\rename_fart_symbols.py  D:\Python\anti-app\frida_fart

参考:使用 Frida 增强 FART:实现更强大的 Android 脱壳能力

word/media/image5.png

重新编译系统

把修改后的 FART 代码替换到 Android 系统里面,重新编译。

# 初始化编译环境
source build/envsetup.sh# 设置编译目标
breakfast wayne# 回到 Android 源码树的根目录
croot# 开始编译
brunch wayne

如何编译 FART ROM 参考这篇文章:移植 FART 到 Android 10 实现自动化脱壳

生成 OTA 包

./sign_ota_wayne.sh

编译完成

word/media/image6.png

刷机

由于我这里是在 WSL 中编译,先把 ota 文件 copy 到 windwos 目录下

cp ./signed-ota_update.zip /mnt/e/lineageos/xiaomi6x_wayne_lineageos-17.1_signed-ota_update_fart_cyrus.zip

设备进入 recovery 模式(或者同时按住【音量+】和【开机键】)

adb reboot recovery

【Apply update】【Apply from adb】开启 adb sideload

word/media/image7.png

开始刷机

adb sideload E:\lineageos\xiaomi6x_wayne_lineageos-17.1_signed-ota_update_fart_cyrus.zip

成功刷入后重启手机。

测试

可以看到 so 中已经检测不出 FART 特征

word/media/image8.png

dex 相关文件也没有检测出 FART 特征

word/media/image9.png

使用 frida_fart 发起主动调用

word/media/image10.png

成功脱壳

word/media/image11.png

测试某视频 app 的 ajm 壳成功脱壳,能正常进入 app 没有被 kill 掉

word/media/image12.png

完整源码

开源地址:

  • https://github.com/CYRUS-STUDIO/AndroidExample

  • https://github.com/CYRUS-STUDIO/FART

相关文章:

  • Android Hook技术防范漫谈

  • frida 检测

相关文章:

  • 终结电源反接与压降损耗:理想二极管控制器深度解析
  • 前端基础学习html+css+js
  • ​​技术深度解析:《鸿蒙5.0+:全场景能效的产业革命》​
  • 第二章 机器学习基本概念
  • Vue组件定义
  • 爱其实很简单
  • AtCoder Beginner Contest 399题目翻译
  • 看问题的本质背后是什么?
  • 《操作系统真相还原》——进入内核
  • 自编码器Auto-encoder(李宏毅)
  • Git深入解析功能逻辑与核心业务场景流程
  • 【Redis】数据类型补充
  • [GESP202412 五级] 奇妙数字
  • 017搜索之深度优先DFS——算法备赛
  • 任务23:创建天气信息大屏Django项目
  • 【火山引擎 大模型批量推理数据教程---详细讲解一篇过!】
  • 文言文停词库 | 古文停词库 | 624个简体停词 |文言文python分词库-thulac
  • OS10.【Linux】yum命令
  • PCB制作入门
  • 财管-0-战略和战略管理
  • asp.net 做网站好吗/如何推广平台
  • 一个空间怎么放两个网站/韩国比分预测
  • web网站怎么做超链接/百度一下照片识别
  • 猎场第几集做的网站推广/seo优化培训课程
  • 网站数据接口怎么做/网站关键词排名
  • 网站的建设初步定位/网站代理公司