mcpField
B
bypassing-root-jailbreak-detection

bypassing-root-jailbreak-detection

github.com/trilwu安全17755 次安装12 周前更新
代码仓库

关于 bypassing-root-jailbreak-detection

面向获授权移动应用测试人员的技能,用于定位并分析 Android 和 iOS 应用的 Root 或越狱检测。内容区分应用检查、Frida 检测与硬件证明,并说明硬件证明的限制。

主要功能

  1. 01定位检测所在层级
  2. 02分析 Android Root 检查
  3. 03分析 iOS 越狱检查
  4. 04区分 Frida 检测与 Root 检测
  5. 05说明硬件证明限制
  6. 06记录检测效果与测试范围

适用场景

  1. 01排查应用启动时退出
  2. 02诊断测试设备上的兼容问题
  3. 03为动态检测建立测试环境
  4. 04记录硬件证明造成的覆盖限制

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trilwu/secskills --skill bypassing-root-jailbreak-detection

安装后在 Claude Code 中输入/bypassing-root-jailbreak-detection确认已加载。

详细介绍

绕过 Root 和越狱检测

检测几乎从来都不是单项检查。它通常是在应用中散布的十几项低成本检查,此外,越来越多的应用还会进行一项硬件支持的证明,任何 Hook 都无法触及。逐项 Hook 检查只会陷入打地鼠游戏;关键在于找出应用实际依赖的层。

仅用于测试你获准测试的应用。

适用场景

  • 应用启动时退出、显示“检测到已 Root 的设备”,或悄无声息地降低功能
  • 应用在原厂设备上正常运行,但在你的测试设备或模拟器上无法运行
  • Frida 成功附加后进程立即退出,或者 frida-ps 没有显示任何内容
  • objection 无法进行探索,或者 Hook 几秒后就停止触发
  • 你需要在进行其他测试前,先搭建稳定的检测环境

不适用场景

  • TLS 拦截失败——使用 bypassing-mobile-pinning;代理错误是另一类问题,不过检测也可能伪装成此类问题
  • 更广泛的评估——使用 testing-mobile-applications
  • 特定框架的逆向工程——使用相关的 reversing-* 技能
  • 破解 DRM 或许可机制以盗版应用——不在适用范围内

先确定检测层

检测分布在四个层级,每个层级都需要不同的应对方式。弄清楚触发的是哪一层,最能节省时间。

层级信号应对方式
Java/托管代码检查RootBeer、File.exists("/system/xbin/su")、针对 Magisk 的软件包查询在 Java 层 Hook(objection、Frida)
.so 中的原生检查对 su 执行 stat/access/fopen,扫描 /proc/self/mapsHook libc,或修补 .so
检测插桩工具仅在附加 Frida 时退出;探测端口 27042;扫描线程名称隐藏 Agent,而非 Root
硬件证明Play Integrity、SafetyNet、DeviceCheck、App Attest无法通过 Hook 绕过——见下文
# What does the app reference? Decompile and look before hooking.
apktool d target.apk -o out
rg -n 'RootBeer|isRooted|su\b|magisk|superuser|test-keys|/system/xbin|busybox|xposed|frida' -i out/smali* out/res 2>/dev/null | head -30
rg -n 'SafetyNet|PlayIntegrity|IntegrityManager|attest|DeviceCheck' -i out/ | head

# Native side
unzip -j target.apk 'lib/arm64-v8a/*' -d libs
rg -a -o 'su|/system/bin/su|magisk|frida|gum-js-loop|gmain' libs/*.so | sort -u | head -20

在原生库中找到的字符串会告诉你应用在查找什么;这比盲目 Hook、等着应用退出要高效得多。

Android

先隐藏环境,而不是先 Hook。 隐藏良好的 Root 环境可以在完全不使用 Hook 的情况下绕过大多数检测,而且应用更新后也不会失效。

# Magisk: DenyList the target so the root is invisible to it
#   Settings → Zygisk ON, Enforce DenyList ON, select the target package
# Shamiko (Zygisk module) hides more thoroughly than DenyList alone
# Play Integrity Fix (PIF) module for the attestation layer — see limits below

# Verify what the app can see
adb shell "pm list packages | grep -i magisk"     # should return nothing to the app

然后再 Hook 剩下的检查:

objection -g com.target.app explore
# then: android root disable

frida -U -f com.target.app -l anti-root-bypass.js --no-pause

常见的 Java Hook 点,按通常出现的顺序排列:

// File existence checks — the single most common family
Java.use('java.io.File').exists.implementation = function () {
  const p = this.getAbsolutePath();
  if (/su$|magisk|superuser|busybox|xposed/i.test(p)) return false;
  return this.exists();
};

// Runtime.exec("su") and ProcessBuilder
// Build.TAGS containing "test-keys"
// PackageManager.getPackageInfo for known root packages
// System properties: ro.debuggable, ro.secure, ro.build.selinux

原生检查需要原生 Hook。 如果 Java Hook 已就位,但应用仍然退出,那么检查就在某个 .so 中:

# See which syscall is finding the evidence
frida-trace -U -f com.target.app -i 'stat*' -i 'access' -i 'fopen' -i 'open'
# then edit the generated handlers to lie about the paths it probes

Frida 检测是另一类问题

如果应用在 Root 环境下运行正常,但你一附加它就退出,那么你面对的是插桩检测;隐藏 Root 并无帮助。

检查项应对方法
TCP 27042 端口开放,或 D-Bus 握手响应在随机端口运行 frida-server,或改用 Gadget 而非 Server
/proc/self/maps 中包含 frida-agent、gum重命名 Agent;使用已修补的构建版本
线程名称 gmain、gum-js-loop、pool-frida已修补的 Frida 构建版本会重命名这些名称
扫描 /proc/self/task/*/stat同上
命名管道和 re.frida.server 字符串使用已修补的构建版本
启动后定期重新检查在检查窗口结束后再附加,或 Hook 定时器

实用的做法是使用已修补的 Frida(有专门为此制作的社区构建版本),并注入 Gadget,而不是运行监听服务。如果检测仍然占上风,就退回到静态分析加定向修补 APK 的方式——虽然会失去交互能力,但仍能继续推进。

iOS

objection -g com.target.app explore
# then: ios jailbreak disable

# Tweak-based, system-wide: Liberty Lite, A-Bypass, Shadow (rootless jailbreaks)

iOS 应用会检查什么,以及要隐藏什么:

  • /Applications/Cydia.app、/bin/bash、/usr/sbin/sshd、 /etc/apt、/private/var/lib/apt 是否存在
  • 是否能在沙盒外写入(fopen("/private/jailbreak.txt", "w"))
  • fork() 是否成功;在沙盒应用中它本不应成功
  • dyld 映像列表中是否包含 MobileSubstrate / substitute
  • cydia:// URL scheme 是否可以打开
  • NSFileManager 检查;与原始 stat 相比,这类检查更容易被 Hook

对于原生层,Hook stat、access、fopen、getenv("DYLD_INSERT_LIBRARIES") 和 _dyld_get_image_name;对于 Objective-C 层,Hook NSFileManager 方法。在未越狱设备上,使用 Frida gadget 重新打包(objection patchipa)——并做好触发完整性验证的准备。

你无法 Hook 的内容

硬件支持的完整性验证是硬性边界。 Play Integrity 的 MEETS_STRONG_INTEGRITY、通过 TEE/StrongBox 进行的密钥验证,以及 Apple 的 App Attest,均由硬件密钥签名,并在供应商服务器上验证。 客户端 Hook 可以改变应用读取到的内容,但无法伪造服务器验证的签名。

这在实践中的含义:

  • Magisk 加上 Play Integrity Fix 模块通常可以达到 BASIC 和 DEVICE 完整性。STRONG 则要求引导加载程序未被修改。
  • 如果应用在强完整性验证失败时会拒绝运行,可靠的选择包括:使用未 Root 的设备运行重新打包的应用(但它本身会无法通过签名检查);使用搭载原厂 ROM 的设备,仅进行网络层测试;或者与客户商定在测试期间豁免完整性验证。
  • 申请豁免。 要求提供关闭完整性验证的测试构建,是常见的测试项目请求,成本远低于花上数天时间却受制于 TEE。

应在报告中如实说明,而不是悄悄缩小测试范围:“由于硬件完整性验证,无法对生产构建进行动态插桩;测试覆盖了 X,但未覆盖 Y。”这是关于测试覆盖范围的发现,隐瞒它比承认限制更糟。

检测质量本身也是一项发现

在绕过检测时,记录检测机制的质量——客户会询问,这也应写入报告:

  • 检测失败时是拒绝运行(退出),还是放行(记录日志后继续)?
  • 检查结果会发送到服务器,还是仅在本地受信任?仅在本地执行的检查充其量只是参考。
  • 检查是否集中在一个函数中(可以轻松 Hook),还是分散执行并反复检查?
  • 是否使用硬件完整性验证,还是只依赖文件系统启发式检查?

如果应用的全部 Root 防护就是 File.exists("/system/xbin/su"),应列为低严重性说明;如果使用经过服务器验证的完整性证明,则应给予肯定。

应当摒弃的理由

  • “objection 的绕过没成功,说明检测太强了。” 它能覆盖常见的 Java 检查。接下来看看原生层。
  • “我隐藏了 Root,所以应该能用了。” 如果应用只在 Frida 环境下崩溃,你对抗的是插桩检测,而不是 Root 检测。
  • “可以用 Frida 绕过 Play Integrity。” 硬件支持的验证结果无法这样绕过。Hook 只能改变应用读取到的内容,不能改变 TEE 签署的内容。
  • “我会修补找到的每一项检查。” 分散的检查会反复执行。找到核心决策点,或者直接隐藏运行环境。
  • “Root 检测是一项漏洞。” 它是一种防护措施,而且防护效果较弱。缺少这类检测只是低严重性说明,不应作为主要发现。
  • “我无法对应用进行插桩,所以没什么可报告的。” 应明确报告测试覆盖范围的限制。

参考资料

  • testing-mobile-applications — 本文所解锁的评估工作
  • bypassing-mobile-pinning — 移动应用“突然无法运行”的另一个原因
  • analyzing-binaries — 修补原生检测例程
  • reporting-security-findings — 如何如实说明测试覆盖范围的限制
  • Magisk + Zygisk、Shamiko、objection、Frida(及修补后的构建)、Liberty Lite、Shadow

评论

支持 Markdown