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

article/2025/7/27 13:10:46

版权归作者所有,如有转发,请注明文章出处: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 检测


http://www.hkcw.cn/article/EUUaNgBogP.shtml

相关文章

Azure DevOps 管道部署系列之一本地服务器

Azure DevOps 是一个帮助改进 SDLC(软件开发生命周期)的平台。 在本文中,我们将使用 Azure Pipelines 创建自动化部署。 Azure DevOps 团队将 Azure Pipelines 定义为“使用 CI/CD 构建、测试和部署,适用于任何语言、平台和云平台”。 在这里,我将解释如何在 Azure Dev…

冤家路窄!萨巴伦卡谈再战郑钦文:这次我状态正佳期待复仇 罗马失利后渴望翻盘

在法网女单1/8决赛中,头号种子萨巴伦卡直落两盘晋级,接下来将对阵郑钦文。赛后,萨巴伦卡接受了采访。记者问她是否认为与郑钦文的比赛会是一场硬仗,萨巴伦卡表示,每次与郑钦文交手都很艰难,因为对方是一位出色的球员。她期待着一场精彩的较量,并且非常期待在1/4决赛中与…

遭邻居多次持刀砍门当事人发声: 她说我们是脑控组织, 入侵她大脑, 已被送精神鉴定 警方介入处理

近日,辽宁大连有网友发布视频称,疑似患有精神疾病的邻居多次持刀上门砍其家门。5月31日,当事人刘女士向媒体透露,楼下60多岁的邻居自去年10月搬入后,频繁上门滋扰,声称刘女士一家是“脑控组织”,意图入侵她的大脑。刘女士解释说,她们一家是外地人,去年才搬到这里,为了…

涉嫌歧视中国球迷!波多尔斯基向俱乐部作保证 社媒未回应照常更新 否认种族歧视指控

近日,德国名将波多尔斯基被指涉嫌对中国球迷进行种族歧视。据其所在俱乐部的消息,波多尔斯基否认了这一指控,并保证自己没有做出这种行为。昨日,欧冠决赛在德国举行,波多尔斯基到场观看了比赛。赛后,一位中国博主在酒吧外偶遇波多尔斯基并请求合影。博主称,波多尔斯基停…

热度直追世界杯!五台山上座率超过8成,多名领导冒雨看“苏超” 球迷热情不减

苏A对阵苏B的比赛吸引了15669名球迷涌入五台山体育场。考虑到最近多个苏超赛场都比较火爆,徐州对阵连云港的比赛有22000多名球迷到场;常州对阵扬州的比赛,在常州工学院体育场围栏外也围着一圈又一圈的球迷。五台山理论上只有18600个座位,本场上座率超过84%。即使南京下了一…

清理 pycharm 无效解释器

1. 起因&#xff0c; 目的: 经常使用 pycharm 来调试深度学习项目&#xff0c;每次新建虚拟环境&#xff0c;都是显示一堆不存在的名称&#xff0c;删也删不掉。 总觉得很烦&#xff0c;是个痛点。决定深入研究一下。 2. 先看效果 效果是能行&#xff0c;而且清爽多了。 3. …

c++面向对象第4天---拷贝构造函数与深复制

含有对象成员的构造函数深复制与浅复制拷贝&#xff08;复制&#xff09;构造函数 第一部分&#xff1a;含有对象成员的构造函数 以下是一个学生 类包含日期成员出生日期的代码 #include<iostream> using namespace std; class Date { public:Date(int year,int month…

【STM32F1标准库】理论——定时器/计数器中断

目录 一、定时器/计数器简介 1.通用定时器 2.基本定时器 二、时基单元 三、定时器/计数器结构框图 四、程序运行途中改变分频系数后的时序 1.缓冲器 2.预装器 3.RCC时钟树 五、计时计算方法 一、定时器/计数器简介 定时器可以对输入的时钟进行计数&#xff0c;并在计…

樊振东新球队夺欧冠冠军 萨尔布吕肯再创辉煌

北京时间6月1日晚,欧洲乒联冠军联赛男团决赛中,樊振东新赛季将加盟的萨尔布吕肯以3-1战胜杜塞尔多夫,第三次夺得欧冠冠军。比赛具体比分为:弗朗西斯卡2-3卡尔伯格、莫雷加德3-0邱党、达科约奇克3-2波尔、弗朗西斯卡3-2邱党。此前,德甲联赛萨尔布吕肯乒乓球甲级俱乐部宣布樊…

发明江苏足球联赛的人 一定是个天才 城市荣誉之战

江苏城市足球联赛的创立者堪称天才。江苏是一个由十三个地级市组成的省份,每个城市都有自己的特色和骄傲,彼此之间竞争激烈。此前,江苏省文旅部门制作了一个景点介绍视频,但网友们却开始计算各城市的出现时长,甚至有评论说“宿迁凭什么比扬州多三秒”。然而,在这样一个充…

韩国大选投票将开启 4位前总统发声 政坛局势扑朔迷离

韩国再次迎来大选季,选情充满变数。前总统朴槿惠突然发声,而现任总统尹锡悦则选择沉默。提前投票已经开始,民众排队等待投下自己的一票。最新民调显示,共同民主党的支持率逼近40%,而国民力量党则降至32%。执政党面临困境,主要是因为未能有效解决民生问题,年轻选民对此不…

高档烟酒“人情往来”暗藏权钱交易 违纪破法的开端

端午节临近,各级纪检监察机关针对这一节点部署了一系列纠治“四风”的明察暗访工作,重点查处违规吃喝、违规收送礼品礼金等问题,并对顶风违纪行为进行快速处理。近期,各地通报曝光了一批典型问题,相关人员均受到了严肃处理。从近年来各级纪检监察机关查处的腐败案件来看,…

C++输入与输出技术详解

文章目录 引言一、C标准输入输出流1.1 cin与cout1.2 cerr与clog 二、C风格输入输出函数2.1 scanf与printf2.2 fgets与puts 三、输入输出优化四、总结 引言 在C编程中&#xff0c;输入与输出&#xff08;I/O&#xff09;操作是程序与用户、文件或其他系统组件交互的核心环节。C…

文件管理器+APP:高效管理,便捷浏览

在智能手机的日常使用中&#xff0c;文件管理是一项不可或缺的功能。无论是存储照片、视频、文档&#xff0c;还是管理各种应用程序的数据&#xff0c;一个高效、便捷的文件管理器都能显著提升用户的使用体验。文件管理器APP正是这样一款拥有直观界面和易于使用的文件管理器应用…

http协议与https协议

目录 一、认识URL 二、urlencode和urldecode 三、HTTP协议格式 1.HTTP请求 2.HTTP响应 3.简单获取一个http响应 4.简单使用抓包工具 fiddler 四、简单的http代码 1.小知识点 &#xff08;1&#xff09;recv &#xff08;2&#xff09;首页 2.Http代码书写vision…

深入理解 C++11 中的 std::move —— 移动语义详解(小白友好版)

引言 随着 C11 的引入&#xff0c;语言迎来了一个重要特性——移动语义&#xff0c;极大地提升了程序性能&#xff0c;尤其是涉及资源管理的类&#xff08;如 string、vector 等容器&#xff09;的效率。作为移动语义的核心工具&#xff0c;move 扮演着关键角色。本文将从基础…

振动力学的三类基本问题

振动问题的分类依赖于分类的出发点&#xff0c;本文从系统论的角度来分析振动问题的分类。如图1&#xff0c;一个振动系统&#xff0c;包括三个方面&#xff1a;输入、系统特性&#xff08;或称为系统模型&#xff09;、输出。其中&#xff0c;输入指外界载荷&#xff0c;包括力…

西瓜书第十一章——降维与度量学习

文章目录 降维与度量学习k近邻学习原理头歌实战-numpy实现KNNsklearn实现KNN 降维——多维缩放&#xff08;Multidimensional Scaling, MDS&#xff0c;MDS&#xff09;提出背景与原理重述1.**提出背景**2.**数学建模与原理推导**3.**关键推导步骤** Principal Component Analy…

20250531MATLAB三维绘图

MATLAB三维绘图 三维曲线&#xff1a;plot3功能介绍代码实现过程plot3实现效果 三维曲面空间曲面作图命令&#xff1a;meshmeshgrid语法示例应用meshgrid实操训练 peakspeaks 的基本用法peaks数学表达式实操训练自定义网格大小使用自定义网格 meshMATLAB代码对齐快捷键Ctrli墨西…

郑钦文称打5盘都没问题可惜没有 能量满满晋级八强

6月1日,法网女单16强决战中,中国选手郑钦文经过2小时47分钟的激战,以2-1战胜俄罗斯名将萨姆索诺娃,赢得罗兰-加洛斯的10连胜,首次闯入法网八强。接下来她将对阵萨巴伦卡。首盘比赛中,郑钦文两次被破发后都顽强回破,最终在抢七局中以7-5拿下第一盘。第二盘,郑钦文错失了…