如何通过编程防护安卓应用免受Frida Objection的动态插桩/运行时挂钩
安卓应用防Frida/Objection动态插桩的编程防护方案
针对你遇到的「已做root检测但Objection仍能成功注入」的问题,以下是可落地的编程防护手段,覆盖Frida核心特征检测、Objection特定拦截、运行时保护等维度:
一、Frida核心特征检测
Frida注入会留下明确的进程/模块特征,可通过以下方式检测:
扫描加载的Native模块:遍历进程已加载的SO库,检查是否存在
libfrida-agent.so或libfrida-gadget.so。Native层示例代码:#include <link.h> #include <string.h> #include <stdlib.h> int has_frida_module() { int detected = 0; dl_iterate_phdr([](struct dl_phdr_info* info, size_t size, void* data) { if (strstr(info->dlpi_name, "libfrida-agent.so") || strstr(info->dlpi_name, "libfrida-gadget.so")) { *(int*)data = 1; return 1; // 找到后终止遍历 } return 0; }, &detected); return detected; }Java层可读取
/proc/self/maps文件,逐行查找包含frida的路径,若存在则触发防护逻辑(比如终止应用)。检测Frida进程:遍历系统进程列表,检查是否存在
frida-server或带有frida标识的进程。Java层可通过ActivityManager获取进程信息:public boolean hasFridaProcess(Context context) { ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses(); for (ActivityManager.RunningAppProcessInfo proc : processes) { if (proc.processName.contains("frida")) { return true; } } return false; }
二、Objection特定防护
Objection作为Frida的上层工具,有专属的注入特征,可针对性拦截:
- 检测Objection Agent特征:Objection注入的agent会在内存中留下
objection、frida-gadget等字符串,可通过扫描进程内存查找这些关键字;同时它会创建特定命名的线程,可遍历线程列表检查线程名是否包含objection。 - 拦截Objection默认Hook点:Objection默认会Hook
android.app.Application.attach、java.lang.Runtime.exec等系统方法。你可以在自己的Application类中重写attach方法,加入调用栈检测:@Override public void attachBaseContext(Context base) { super.attachBaseContext(base); // 检查调用栈是否有Frida/Objection相关类 StackTraceElement[] stack = Thread.currentThread().getStackTrace(); for (StackTraceElement elem : stack) { if (elem.getClassName().contains("frida") || elem.getClassName().contains("objection")) { System.exit(0); // 终止应用 } } }
三、运行时内存与调试防护
- 强制禁用调试模式:虽然release版默认
debuggable=false,但攻击者可通过adb强制修改,需在代码中校验:public boolean isDebuggable(Context context) { return (0 != (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)); } // 在Application onCreate中调用 if (isDebuggable(this)) { System.exit(0); } - 内存区域保护:将核心业务的Native代码内存区域设置为只读可执行,防止Frida修改。使用
mprotect函数实现:#include <sys/mman.h> void protect_core_code() { // 假设core_func是你的核心函数地址 void* start = (void*)((uintptr_t)&core_func & ~(PAGE_SIZE - 1)); size_t size = PAGE_SIZE; mprotect(start, size, PROT_READ | PROT_EXEC); }
四、代码混淆与敏感信息保护
- 开启ProGuard/R8混淆:混淆类名、方法名,特别是核心防护类和业务类,增加Frida Hook的难度。确保混淆规则中不排除关键类。
- Native代码混淆:使用O-LLVM等工具对Native代码进行控制流平坦化、指令替换,让攻击者难以分析逻辑。
- 敏感字符串加密:将检测用的关键字(比如
frida、objection)加密存储,运行时再解密使用,避免静态分析直接获取。比如Native层用简单的异或加密:char* decrypt_string(char* encrypted, int key) { char* decrypted = malloc(strlen(encrypted) + 1); for (int i=0; encrypted[i]; i++) { decrypted[i] = encrypted[i] ^ key; } decrypted[strlen(encrypted)] = '\0'; return decrypted; } // 使用时 char* frida_keyword = decrypt_string("\x66\x72\x69\x64\x61", 0x10); // 加密后的"frida" if (strstr(info->dlpi_name, frida_keyword)) { // 触发防护 } free(frida_keyword);
五、反Ptrace附着检测
Frida通过Ptrace机制附着进程,可检测是否被其他进程Ptrace:
int is_traced() { FILE* fp = fopen("/proc/self/status", "r"); if (!fp) return 0; char buf[256]; while (fgets(buf, sizeof(buf), fp)) { if (strstr(buf, "TracerPid:")) { int pid = atoi(buf + strlen("TracerPid:")); fclose(fp); return pid != 0; } } fclose(fp); return 0; }
检测到被Ptrace时,直接终止应用或执行自定义防护逻辑。
内容的提问来源于stack exchange,提问作者DAC84
相关产品推荐
相关产品推荐

