当前位置:首页>学习笔记>Frida学习笔记(十六):Root 检测绕过

Frida学习笔记(十六):Root 检测绕过

  • 2026-05-30 22:28:12
Frida学习笔记(十六):Root 检测绕过

解决一件事:App 在 Root 设备上启动即闪退时,让它继续跑。聚焦设备本地的 Root 痕迹检测——文件、Shell 命令、系统属性、包名、Native 层 syscall。

配套脚本:本篇产出的整套 Root 检测绕过脚本(Java 四维 Hook + Native 层 libc Hook + 酷狗 / 招行双实战绕过 + RUN.md 真机验证清单,复制即用)已整理打包。关注本公众号后私信回复关键词「脚本」 即可获取,本系列与后续 Unidbg / SO 逆向 / ARM 汇编 等系列脚本会统一在此发放并持续更新。

一、检测强度分级

  • 一般商业 App:Java 层基础文件 + 包名检测,最简单的脚本即可。

  • 游戏App:防 GameGuardian 修改内存、防模拟器刷号,Root + 模拟器双检为主。

  • 金融App:合规要求驱动,普遍接入同盾、盾山、梆梆等第三方 SDK,Java + Native 双层 + 后台持续检测。

检测强度分级模型

边界:本文范围是设备本地检测——App 进程内的判定逻辑都可以靠 Hook 改返回值。但 2025 年 1 月 SafetyNet 关停后,银行/支付/政务类 App 主流已切换到 Play Integrity API + 硬件密钥认证 (Key Attestation) 双重把关:

  • Play Integrity 的 verdict(MEETS_BASIC / DEVICE / STRONG_INTEGRITY)是 Google 服务端签名后返回的 JWT,App 把它转发给业务后端校验。Hook App 本地无效——签名密钥不在 App 里。
  • Hardware Key Attestation 通过 KeyStore.attestKey() 拿一条 TEE/StrongBox 出厂时刻入的硬件证书链,业务后端能据此判出"设备 bootloader 是否解锁过 / 是否换过 ROM"。几乎无法软件绕过,业内目前只能靠 PlayIntegrityFix Magisk 模块伪造 BASIC/DEVICE verdict,STRONG 拿不到。

也就是说,本文对付的是自家测试 / CTF / 非 Google Play 渠道的商业 App。强制 STRONG 的银行 App 不靠"多 Hook 几个点"——靠 Magisk 模块 + 干净硬件指纹 + ZygiskNext 隐藏链。

二、Root 检测的四个维度

Root 痕迹分四类——su 等特征文件、which su 类 Shell 命令、ro.build.tags=test-keys 等系统属性、Magisk Manager 这类包名。下图给出每类的关键证据和对应 Hook 点,第 2.1 节 – 第 2.4 节 一一对应展开:

Root 检测四维全景

同一个 App 经常两层都做一遍:Java 层走 File.exists / Runtime.exec;Native 层走 libc 的 access / stat / fopen,触发时机比 Java 层早。下图按 App 启动时间轴把两层并排画出来,每个检测点旁标了 Hook 入口:

Java 层与 Native 层检测架构

时间轴上 Native 检测在 JNI_OnLoad / SO 静态构造里就跑(T2),等 Java 检测线程起来(T3+)再 Hook 已经晚了——所以 Hook 必须赶在 native 检测之前就位,而 native 又早于 Java,唯一稳妥的办法是默认上 spawn(详见 第 4.2 节)。

拿到样本第一步要决定从哪层下手——按症状选策略,免得用银行 App 那套去打一个 Java 层小工具。下图把"症状 → 入手层"拆成三条决策路径:

Hook 从哪一层下手的三条决策路径

简而言之:Java 层闪退或弹 Toast,先 第 2.1 节 - 第 2.4 节;只有 native 痕迹(栈干净 / 信号文件 / Process 异常)才下到 第 3 章;分不清就 spawn 起来双层都挂,等命中再砍多余的 Hook。

2.1 文件存在性检测

Root 操作会留下特征文件——su 二进制、Magisk 管理文件、SuperSU 的 APK 等。App 通过 File.exists() 检查这些路径。

常见路径清单(备查,实战 Hook 用下面的通配):

su 二进制文件(含 Android 10+ 新分区):
  /system/bin/su, /system/xbin/su, /sbin/su
  /data/local/su, /data/local/bin/su, /data/local/xbin/su
  /system_ext/bin/su          ★ Magisk systemless 在 Pixel / Android 10+ 的常见位置
  /product/bin/su, /odm/bin/su
  /apex/com.android.runtime/bin/su, /apex/com.android.art/bin/su

Root 管理工具:
  /system/app/Superuser.apk, /system/app/SuperSU.apk
  /sbin/magisk, /sbin/.magisk
  /data/adb/magisk, /data/adb/magisk.db
  /data/adb/ksu, /data/adb/ksud        # KernelSU
  /data/adb/ap, /data/adb/apd          # APatch

BusyBox(Root 常见伴随工具):
  /system/xbin/busybox, /system/bin/busybox

Frida 自身:
  /data/local/tmp/frida-server

把上面这份清单写成精确匹配的数组挡不住 Android 10+ 的 $PATH 扫描——酷狗 SystemUtils.Q6() 直接读 System.getenv("PATH") 切分查 su,Pixel 上 PATH 含 /system_ext/bin,Magisk systemless 装的 su 就在那(详见 第 5 章)。Hook 改用通配判定,命中 /su 结尾或 magisk 子串就一律 false:

Java.perform(function() {
    var File = Java.use("java.io.File");

    // 通配:路径以 /su 结尾,或包含 magisk —— 不要扩大到 root/sys/security 等关键字,
    // 会误伤业务正常文件
    function isRootPath(p) {
        return p && (p.endsWith("/su"|| p.indexOf("magisk"!== -1);
    }

    // exists / canRead / canExecute 三件套都要 hook
    // 酷狗 SystemUtils.P6/Q6 用的是 f.exists() && f.canExecute()
    // 若只 hook exists,App 写 if (f.canExecute()) return true; 就漏检
    File.exists.implementation = function() {
        var p = this.getAbsolutePath();
        if (isRootPath(p)) {
            console.log("[Root] File.exists → false: " + p);
            return false;
        }
        return this.exists();
    };
    File.canExecute.implementation = function() {
        var p = this.getAbsolutePath();
        if (isRootPath(p)) return false;
        return this.canExecute();
    };
    File.canRead.implementation = function() {
        var p = this.getAbsolutePath();
        if (isRootPath(p)) return false;
        return this.canRead();
    };
});

File.exists() 无参,路径在 File 对象内部,用 this.getAbsolutePath() 取。通配只锁 /su$magisk——扩到 sys 会命中 /system/.../sys/... 几乎所有系统路径,扩到 root / security 也会撞上一堆正常的库名和缓存目录,招行 dRbf 那种反复 new File(...).exists() 探业务文件的逻辑首当其冲。

2.2 Shell 命令检测

which su——如果 su 在 PATH 中存在会返回路径,否则返回空。或者直接执行 su -c id,成功返回 uid=0 即视为已 Root。

Hook Runtime.exec(),拦截含危险关键字的命令:

Java.perform(function() {
    var Runtime = Java.use("java.lang.Runtime");

    Runtime.exec.overloads.forEach(function(overload) {
        overload.implementation = function() {
            var cmd = arguments[0];
            var cmdStr = "";
            if (typeof cmd === "string") {
                cmdStr = cmd;
            } else if (cmd !== null && cmd.length !== undefined) {
                var parts = [];
                for (var i = 0; i < cmd.length; i++) parts.push(cmd[i]);
                cmdStr = parts.join(" ");
            }

            var dangerous = ["su""which""magisk""busybox""supersu",
                             "test-keys""mount""remount"];
            var cmdLower = cmdStr.toLowerCase();
            for (var j = 0; j < dangerous.length; j++) {
                if (cmdLower.indexOf(dangerous[j]) !== -1) {
                    console.log("[Root] Runtime.exec 拦截: " + cmdStr);
                    return Runtime.exec.call(this"echo");
                }
            }

            return overload.apply(this, arguments);
        };
    });
});

替换成 echo 而非返 nullRuntime.exec 返回 Process,调用方一般会读它的输出流,null 触发 NPE 会让 App 崩。echo 输出空、退出码 0,等价于 su 不存在。

2.3 系统属性检测

ro.build.tags:debug build 是 test-keys,正式版是 release-keys,Root 的自定义 ROM 多半是 test-keys

读取有两条路径,都要拦:

  • SystemProperties.get("ro.build.tags")
  • android.os.Build.TAGS 静态字段直读

比如酷狗 FireEye SDK 的 DeviceInfo.isDeviceRooted 走的是后者(String tags = Build.TAGS;),单 Hook SystemProperties.get 拦不住。

Java.perform(function() {
    // ===== 路径 A:SystemProperties.get =====
    try {
        var SystemProperties = Java.use("android.os.SystemProperties");
        var fakeValues = {
            "ro.build.tags": "release-keys",
            "ro.debuggable": "0",
            "ro.secure": "1",
            "ro.build.type": "user",
            "ro.build.selinux": "1",
            // verified boot 三件套(金融类 SDK 常查)
            "ro.boot.verifiedbootstate": "green",
            "ro.boot.flash.locked": "1",
            "ro.boot.veritymode": "enforcing",
        };

        SystemProperties.get.overload("java.lang.String")
            .implementation = function(key) {
            if (fakeValues[key] !== undefined) {
                console.log("[Root] 属性伪造: " + key + " → " + fakeValues[key]);
                return fakeValues[key];
            }
            return this.get(key);
        };
        SystemProperties.get.overload("java.lang.String""java.lang.String")
            .implementation = function(key, def) {
            if (fakeValues[key] !== undefinedreturn fakeValues[key];
            return this.get(key, def);
        };
    } catch(e) {}

    // ===== 路径 B:android.os.Build 静态字段反射改值 =====
    // Build.TAGS / Build.FINGERPRINT / Build.TYPE 是 public static final,
    // 但通过反射 + setAccessible 仍可改。改完后所有 new String 形式的读取都拿到新值。
    try {
        var Build = Java.use("android.os.Build");
        var fakeBuildFields = {
            "TAGS": "release-keys",       // FireEye / RootBeer 等直读 Build.TAGS
            "TYPE": "user",                // 部分 SDK 读 Build.TYPE 判 user/userdebug
            // FINGERPRINT 不强制改(很多 App 用它做设备指纹上报,乱改可能干扰业务)
        };
        Object.keys(fakeBuildFields).forEach(function(name) {
            try {
                var field = Build.class.getDeclaredField(name);
                field.setAccessible(true);
                field.set(null, Java.use("java.lang.String").$new(fakeBuildFields[name]));
                console.log("[Root] Build." + name + " → " + fakeBuildFields[name]);
            } catch(e) {
                console.log("[!] Build." + name + " 反射失败: " + e.message);
            }
        });
    } catch(e) {}
});

API 30+ 反射可能失败:Android 11 把 Field.modifiers 列入 hidden API 黑名单,普通 App 拿不到 modifiers 字段去清 final 标志位;部分字段在 JIT/AOT 优化后会被 inline 成常量。两个补救路径

  • 从源头:Build 字段的值大多来自 SystemProperties.get("ro.product.model") 等属性读取,把 ro.build.tags / ro.product.model / ro.product.brand 等键加进路径 A 的 fakeValues,比改字段更稳
  • 拦调用方:直接 Hook 那个读了 Build.TAGS 做判定的方法,从结果端绕过(参见 第 4.3 节 输出口法)

实测 Pixel 6 Pro / Android 13 / Magisk + Zygisk 上反射能成功(酷狗案例 RUN.md 记录这段反射写入的 Build 字段——TAGS / TYPE——写完读回一致),但未 root 设备 / 严格 hidden API 检查的 App 仍可能静默失败。判别方式:写完后立刻 console.log(Build.TAGS.value) 读回看,相等就成。

2.4 包名检测

PackageManager 检查 Magisk Manager(com.topjohnwu.magisk)、SuperSU(eu.chainfire.supersu)等是否安装:

Java.perform(function() {
    var PM = Java.use("android.app.ApplicationPackageManager");
    var NNFE = Java.use("android.content.pm.PackageManager$NameNotFoundException");

    var hiddenPackages = [
        "com.topjohnwu.magisk",
        "io.github.vvb2060.magisk",
        "eu.chainfire.supersu",
        "com.koushikdutta.superuser",
        "com.noshufou.android.su",
        "com.thirdparty.superuser",
        "com.yellowes.su",
        "com.kingroot.kinguser",
        "de.robv.android.xposed.installer",
        "org.meowcat.edxposed.manager",
        "com.saurik.substrate",
    ];

    function isHidden(name) {
        for (var i = 0; i < hiddenPackages.length; i++)
            if (name === hiddenPackages[i]) return true;
        return false;
    }

    function filterList(list) {
        var it = list.iterator();
        while (it.hasNext()) {
            var name = it.next().packageName.value;
            if (isHidden(name)) {
                it.remove();
                console.log("[Root] 从列表移除: " + name);
            }
        }
        return list;
    }

    // ===== 点查路径:getPackageInfo =====
    // 老重载 (String, int)
    PM.getPackageInfo.overload("java.lang.String""int")
        .implementation = function(name, flags) {
        if (isHidden(name)) {
            console.log("[Root] 隐藏包: " + name);
            throw NNFE.$new(name);
        }
        return this.getPackageInfo(name, flags);
    };
    // Android 13+ (API 33) 新重载 PackageInfoFlags:targetSdk ≥ 33 走新签名
    try {
        PM.getPackageInfo.overload("java.lang.String",
                                   "android.content.pm.PackageManager$PackageInfoFlags")
            .implementation = function(name, flags) {
            if (isHidden(name)) {
                console.log("[Root] 隐藏包(Flags 重载): " + name);
                throw NNFE.$new(name);
            }
            return this.getPackageInfo(name, flags);
        };
    } catch(e) {}

    // ===== 枚举路径:getInstalledPackages + getInstalledApplications =====
    PM.getInstalledPackages.overload("int")
        .implementation = function(flags) {
        return filterList(this.getInstalledPackages(flags));
    };
    try {
        PM.getInstalledPackages
          .overload("android.content.pm.PackageManager$PackageInfoFlags")
          .implementation = function(flags) {
            return filterList(this.getInstalledPackages(flags));
        };
    } catch(e) {}

    // getInstalledApplications 返回 ApplicationInfo 列表,packageName 字段同名,filterList 直接复用
    try {
        PM.getInstalledApplications.overload("int")
            .implementation = function(flags) {
            return filterList(this.getInstalledApplications(flags));
        };
        PM.getInstalledApplications
          .overload("android.content.pm.PackageManager$ApplicationInfoFlags")
          .implementation = function(flags) {
            return filterList(this.getInstalledApplications(flags));
        };
    } catch(e) {}
});

三、Native 层文件检测

SDK 在 Native 层直接调 libc 的 access() / stat() / open() / fopen(),绕过 Java 层。Android 11+ Bionic 主推 *at 系列(openat / faccessat / fstatat,POSIX.1-2008 atfd 风格),新 NDK 编译的 SO 大量走 *at——只 Hook access/stat 会漏。

锁定 Frida 16;Frida 17 起 Module.findExportByName 已 deprecated,改用 Process.getModuleByName("libc.so").getExportByName(...)

(function() {
    // ============ 路径判定 ============
    // 以 /su 结尾、/su/ 中段、或 magisk/supersu/busybox/xposed 子串。
    // 用 endsWith / "/su/" 中段判定,避免 indexOf("/su") 误命中 /subsystem/、/sys/.../subsys 等正常路径。
    // 注:native 层 isRootPath 比第 2.1 节 Java 层多加了 supersu/busybox/xposed 三个关键字——它们是 root
    // 工具的专用名,业务代码极少撞名;Java 应用层会调 File.exists 看一般业务文件(如招行 dRbf),扩关键字
    // 会误伤,所以那边只锁 /su+magisk。Native 层调 access/stat 的几乎只剩 SDK 检测代码,边界更窄,可以放宽。
    function isRootPath(path) {
        if (!path) return false;
        if (path === "/su" || path.endsWith("/su")) return true;
        if (path.indexOf("/su/"!== -1return true;       // /system/su/bin/... 也算
        if (path.indexOf("magisk"!== -1return true;
        if (path.indexOf("supersu"!== -1return true;
        if (path.indexOf("busybox"!== -1return true;
        if (path.indexOf("xposed"!== -1return true;
        return false;
    }

    // ============ ① access / faccessat —— 文件可达性 ============
    // faccessat 路径是 args[1](args[0] 是 dirfd);faccessat 比 access 更常见
    [
        { name: "access",    pathArgIdx: 0 },
        { name: "faccessat", pathArgIdx: 1 },
    ].forEach(function(spec) {
        var p = Module.findExportByName("libc.so", spec.name);
        if (!p) return;
        Interceptor.attach(p, {
            onEnter: function(args) {
                var path = Memory.readUtf8String(args[spec.pathArgIdx]);
                this.block = isRootPath(path);
                if (this.block) console.log("[Native] " + spec.name + " 拦截: " + path);
            },
            onLeave: function(retval) {
                if (this.block) retval.replace(ptr(-1));
            }
        });
    });

    // ============ ② stat / lstat / stat64 / lstat64 / fstatat —— 文件元数据 ============
    // ARM64/Android 14 实测 stat64/lstat64 仍导出,全 5 个挂上
    [
        { name: "stat",     pathArgIdx: 0 },
        { name: "lstat",    pathArgIdx: 0 },
        { name: "stat64",   pathArgIdx: 0 },
        { name: "lstat64",  pathArgIdx: 0 },
        { name: "fstatat",  pathArgIdx: 1 },   // dirfd 在 args[0]
    ].forEach(function(spec) {
        var p = Module.findExportByName("libc.so", spec.name);
        if (!p) return;
        Interceptor.attach(p, {
            onEnter: function(args) {
                var path = Memory.readUtf8String(args[spec.pathArgIdx]);
                this.block = isRootPath(path);
            },
            onLeave: function(retval) {
                if (this.block) retval.replace(ptr(-1));
            }
        });
    });

    // ============ ③ open / openat —— 打开文件读 ============
    // open(path, flags, ...) / openat(dirfd, path, flags, ...)
    [
        { name: "open",   pathArgIdx: 0 },
        { name: "openat", pathArgIdx: 1 },
    ].forEach(function(spec) {
        var p = Module.findExportByName("libc.so", spec.name);
        if (!p) return;
        Interceptor.attach(p, {
            onEnter: function(args) {
                var path = Memory.readUtf8String(args[spec.pathArgIdx]);
                this.block = isRootPath(path);
            },
            onLeave: function(retval) {
                if (this.block) retval.replace(ptr(-1));    // -1 + errno = ENOENT
            }
        });
    });

    // ============ ④ fopen —— stdio 包装 ============
    // 招行 parse_self_maps 用 fopen("/proc/self/maps","r")+fgets 的范式
    // 很多 root 检测也用 fopen 读 /proc/.../status、/proc/mounts
    var fopenPtr = Module.findExportByName("libc.so""fopen");
    if (fopenPtr) {
        Interceptor.attach(fopenPtr, {
            onEnter: function(args) {
                var path = Memory.readUtf8String(args[0]);
                this.block = isRootPath(path);
            },
            onLeave: function(retval) {
                if (this.block) retval.replace(ptr(0));     // NULL = 打开失败
            }
        });
    }
})();

少数高强度加固 SDK 会用 svc 0 直接走 syscall,整层绕开 libc Hook —— 它们不调 access/stat/open*,而是用 ARM64 的 mov x8, #SYS_openat; svc 0 内联汇编直接发起系统调用,完全不经过 libc。Interceptor.attach("libc.so", "openat") 在它们身上不会触发任何回调。这是个老技术(Windows 侧的 direct syscall、ARM64 侧的 svc 直调都存在多年),不是某年的新拐点;真正大量用它的也只是个别方案(见本节末)。

挡 svc 0 的两条路径

  • Stalker 指令级追踪:用 Stalker.follow() 扫描每条指令,在 svc 指令前插桩。性能开销大,精度最高。详见第 24 篇。
  • Interruptor 库:基于 Stalker 封装的 syscall 级 Hook,API 类似 Interceptor 但作用在 svc 上。GitHub FrenchYeti/interruptor,几行代码就能 strace 一个进程的全部 syscall 并按号过滤。

判定目标是否用了 svc 0 的最快方法:Hook 完上面这段后,如果 App 仍然准确判出"已 Root"且能定位到某个 SO 模块,在那个 SO 里 r2 -Asvc #0 指令——命中数十处且围绕 Root 路径字符串,就是它:

$ r2 -A libCheck.so
[0x00012340]> /ad svc #0 # 搜 svc 0 指令
0x00018a40   d4000001  svc 0      # ← 旁边能看到 mov x8, #56 (SYS_openat)
0x00018c20   d4000001  svc 0      # ← 附近字符串 "/system/bin/su"
0x00019100   d4000001  svc 0
... 28 hits in libCheck.so

像招行 libCmbShield.so 这种只有零星几条 svc 0、主要靠 plt_hook 自家 libc 的情况,上面脚本能拦住绝大部分。只有同盾、网易易盾的部分版本会大量用 svc 0 把 libc Hook 整层绕掉。

四、对抗升级与方法论

第 2、3 章 的代码模板抄了就能用,但跑之前先看三件事:

  • 装了 Shamiko / Zygisk Assistant 的设备,root 痕迹在 mount namespace 层面已被挖掉,Java 层 Hook 大半冗余(第 4.1 节)
  • 检测线程在 JNI_OnLoad 就跑完,attach 来不及——默认上 spawn,有必要再拦线程(第 4.2 节)
  • 加固 SDK 不逐点绕,找业务侧调用的输出方法改返回值(第 4.3 节)

第 5、6 章 两个实战分别落在这套方法论的"够用"和"边界"两侧。

4.1 反直觉:装了 Shamiko,第 2、3 章 大部分 Hook 反而冗余

如果你写了一套 200 行脚本,跑在自己设备上发现"啥都不用 Hook,App 也不闪退"——不是脚本生效了,是 Shamiko(或 Zygisk Assistant 对 KernelSU/APatch 同理)在更早的 zygote 注入阶段,直接把 root 痕迹从 App 进程的 mount namespace 里整体挖掉了。

具体做了什么:

  • bind mount 用空目录覆盖 /data/adb/sbin/magisk 等路径,App 真的看不到这些文件——File.exists() 返回 false 是真返回 false,不是 Hook 出来的
  • com.topjohnwu.magisk 从该 App 视图的 PackageManager 里隐藏(基于 <queries> 的精确点查也拿不到)
  • 重置 /proc/self/status/proc/self/mountinfo 等暴露源
  • 阻断 Zygisk API 在 DenyList 名单 App 内的暴露

影响:

  • 第 2.1、2.2、2.4、3 章 在 Shamiko 设备上是重复保险——root 痕迹已在 mount namespace 层被挖掉,不写也大概率 OK。但这只在装了 Shamiko 时成立:酷狗(第 5 章)是纯 Magisk、没装 Shamiko 的环境,/system_ext/bin/su 这个 symlink 明摆在那照样命中——这种环境下第 2、3 章的 Hook 一个都不能省
  • 第 2.3 节(系统属性 / Build 字段)不在重复保险之列——Shamiko 不改 build 属性,FireEye 直读 Build.TAGS 还得靠 第 2.3 节 路径 B 反射拦
  • 高强度 SDK 会上反 Shamiko 手段:行为指纹(/proc/self/status/proc/self/maps 横向交叉验证)、timing 异常(open root 路径 vs 普通路径耗时差)、特定 mount 点扫描(找 Shamiko 自己的痕迹)。有矛就有盾——这些手段跟随 Shamiko 出现就陆续在上,程度不一。第 2、3 章 脚本真正起作用的就是拦这些残留

实际顺序:先把 root 方案配齐(Magisk/KernelSU + Zygisk Assistant + 目标 App 加入 DenyList),再上 Frida 处理残留。反过来做的话会写一堆没用的脚本。

4.2 多线程检测的时序问题

高强度方案在 App 启动时起后台线程循环检测。Hook 装得再全,只要检测线程在 Hook 之前先跑了一遍并记下结论,后面 Hook 就晚了。

下图把 App 启动过程切成 5 个时间点:Attach 最早只能在 T3 之后接入,T2 的 JNI_OnLoad Native 检测已经成为既定事实。

Spawn vs Attach 时序窗口

策略一:用 Spawn 模式,确保绕过脚本在 App 任何代码执行前就生效。

策略二:直接阻止检测线程的启动:

Java.perform(function() {
    var Thread = Java.use("java.lang.Thread");
    Thread.start.implementation = function() {
        var name = this.getName();
        var threadClass = this.getClass().getName();

        // SafetyNet 已于 2025-01 关停,从关键字里删掉避免误命中过渡期残留线程
        var suspicious = ["security""integrity""check",
                          "detect""guard""protect""PlayIntegrity"];
        var nameLower = (name + " " + threadClass).toLowerCase();
        for (var i = 0; i < suspicious.length; i++) {
            if (nameLower.indexOf(suspicious[i]) !== -1) {
                console.log("[线程] 阻止: " + name + " (" + threadClass + ")");
                return;
            }
        }
        this.start();
    };
});

这套关键字会误杀业务线程,先把 return 改成只打日志跑一遍,确认哪些是检测线程再开拦截。招行案例 6 个检测都在 JNI_OnLoad 启动,attach 模式根本来不及——所有需要绕过强检测的脚本默认上 spawn。

4.3 第三方安全 SDK 的应对思路

同盾、盾山、梆梆这些 SDK 内部混淆且加固,逐点分析不现实。直接绕过它的结果输出函数——SDK 最终总会通过某个方法返回 boolean 或风险评分,改这一个返回值就够。

加固 SDK 输出口四步法 · 主路径 4 步 + 两类找错回溯

定位输出方法的四步:

Step 1:定位 SDK 边界类——jadx 打开 APK,Ctrl+Shift+F 输入包名前缀(如 com.tongduncom.bangcle),找名字最朴素的那个(通常叫 SecurityClient / RiskDetector / SafeSDK)。

Step 2:找出输出方法——边界类里搜返回 boolean 或返回 int 的 public 方法:

isSecure() / isSafeEnv() / checkEnvironment()    → 返回 boolean
getRiskLevel() / getDeviceScore() → 返回 int(评分越高越危险)

Step 3:找业务侧调用点——Find Usage 跳到调用位置,确认"返回什么值 = 安全":

if (!SafeSDK.getInstance().isSecure()) {
    finish();  // 不安全就闪退 → 让 isSecure() 返回 true
}
// 或:
if (RiskDetector.getRiskLevel() > 60) {
    showRiskDialog();  // → 让评分 ≤ 60
}

Step 4:一行 Frida Hook 收工

Java.use("com.example.security.SafeSDK").isSecure.implementation = function() {
    return true;
};

找错输出口的表现:脚本加载成功、log 显示 Hook 生效,但 App 照样闪退——SDK 里还有别的输出函数。回 Step 2 把候选列出来对照。两种典型的"找错":

  • 酷狗(第 5 章):grep 出来的第一个候选 FireEye isDeviceRooted 是"迷雾"——它确实在跑,但返 false(漏检),真凶是另一个 SystemUtils.O6。靠 第 5 章 Toast 反查才定位到。
  • 招行(第 6 章):命中点根本不在调用栈里——native 命中只写信号文件,Java onCreateFile.exists() 弹窗,Toast 栈干净到查不到 native 痕迹。这种延迟反馈得搜 Cts.A / Cts.B 常量的 caller,或 hook 所有 new File().exists() 看哪些路径被访问。

五、酷狗音乐

目标:com.kugou.android v20649。环境:Pixel 6 Pro / Android 13 / Magisk。症状:启动后短暂弹 Toast「当前处于 root 环境,请注意账号安全」。

不阻塞登录,但 SDK 会上报到服务器风控。下面只讲检测点和绕过——反查过程(grep / Toast.show 抽栈 / jadx 跳栈)就是第 4.3 节四步法 + 第 5 章 Toast 反查的应用,不展开。

5.1 检测点:SystemUtils.O6 = P6 || Q6

KgUserLoginAndRegActivity.onCreate 里调一次 SystemUtils.O6(),命中就弹 Toast:

public static boolean O6()  { return P6() || Q6(); }

private static boolean P6() {
    String[] dirs = {"/system/bin/""/system/xbin/""/system/sbin/""/sbin/""/vendor/bin/"};
    for (String d : dirs) {
        File f = new File(d + "su");
        if (f.exists() && f.canExecute()) return true;
    }
    return false;
}

private static boolean Q6() {
    for (String d : System.getenv("PATH").split(":")) {
        File f = new File(d, "su");
        if (f.exists() && f.canExecute()) return true;
    }
    return false;
}

两层:

  • P6 —— 固定 5 个目录拼 "su" 后扫,exists() && canExecute() 同时成立才算
  • Q6 —— 读 System.getenv("PATH") 切分,每个目录里查 su

Pixel 6 Pro 的 PATH 包含 /system_ext/bin,Magisk systemless 装的 symlink 就在那:

$ adb shell "ls -la /system_ext/bin/su"
lrwxrwxrwx 1 root root /system_ext/bin/su -> ./magisk

→ P6 五个固定目录都不命中(systemless 把老路径抹掉了),Q6 通过 $PATH 扫到 /system_ext/bin/suO6 = true,Toast 弹。

反查路径速记:先 grep 文案(注意 strings 默认只输出 ASCII,中文 UTF-8 字节会被切掉,要 strings -e Sgrep -aoF),找到 classes16.dex 后用第 5 章 Toast 反查抽栈定位到 onCreate 那一行。完整过程在酷狗的无损分析文档里,本节不展开。

5.2 两层防御 + ART AOT inline

A 拦根因(File.exists),B 拦业务输出口(O6/P6/Q6),不再叠 Toast hook(只遮盖症状不阻止检测):

Java.perform(function() {
    var File = Java.use("java.io.File");

    // ★ Deopt 触发器:给 Activity.onCreate 挂 no-op hook,强制 ART 把这个方法降级到解释器,
    // 让 ART AOT inline 进 onCreate 的 O6/P6/Q6 重新走 ArtMethod 派发,下面的方案 B 才有机会响应
    try {
        Java.use("com.kugou.common.useraccount.app.KgUserLoginAndRegActivity")
            .onCreate.implementation = function(b) { this.onCreate(b); };
    } catch(e) {}

    // A: File.exists / canExecute 通配拦截——根因,标准库不被 inline,hook 必中
    File.exists.implementation = function() {
        var p = this.getAbsolutePath();
        if (p && (p.endsWith("/su"|| p.indexOf("magisk"!== -1)) return false;
        return this.exists();
    };
    File.canExecute.implementation = function() {
        var p = this.getAbsolutePath();
        if (p && (p.endsWith("/su"|| p.indexOf("magisk"!== -1)) return false;
        return this.canExecute();
    };

    // B: 业务侧输出口——配合上面的 deopt 触发器才会真正生效
    try {
        var SU = Java.use("com.kugou.common.utils.SystemUtils");
        SU.O6.implementation = function() { return false; };
        SU.P6.implementation = function() { return false; };
        SU.Q6.implementation = function() { return false; };
    } catch(e) {}
});

关键论断:无论 B 是否生效,A 都把 toast 兜下来。这是本案例脚本最值得记的工程结论:

B hook 状态O6/P6/Q6 原代码内部 File.exists 调用toast 结果
✓ 生效(带 deopt 触发器)不跑(被 frida 替换)不发生不弹(B 拦掉)
✗ 被 ART AOT inline 跳过照跑(展开在 onCreate 里)发生 → A 全返 false不弹(A 让 rooted=false

实测两种状态:

  • 装了 deopt 触发器:[A]触发20次 [B]触发1次(A 少 14 条因为 P6/Q6 直接被 B 短路了)
  • 没装 deopt 触发器:[A]触发34次 [B]触发0次(B 形同虚设,A 是主力)

两种状态下 toast 都不弹。A 是这套脚本的硬兜底——File 是 Java 标准库,ART 不 inline,hook 必中;B 是锦上添花——P6/Q6 万一改了名 A 也照样把底层 File.exists 截住。

为什么 B 默认不工作 → ART AOT inlineO6/P6/Q6 都是 private static,体积小,是典型的 inline 候选。ART 在装包时把它们的字节码直接展开到 KgUserLoginAndRegActivity.onCreate 的 native 机器码里——Frida 的 .implementation 挂在 ArtMethod 派发入口,inline 后 CPU 不经过 ArtMethod,hook 永远不响。加 Activity.onCreate no-op hook 让 Frida 触发该方法 deopt 后,ART 丢掉 AOT 机器码降级到解释器执行,解释器逐条解释字节码看到 invoke-static O6 才老实走 ArtMethod 派发,B 才复活。

ART AOT inline 让 Frida hook 失效 · dex / AOT 后机器码 / deopt 后解释器 三层状态对比

通用经验:hook 业务侧的 small-private-static 检测方法装上没报错但收不到事件,第一反应是 ART AOT inline——给持有它的调用方(一般是 Activity.onCreate)挂个 no-op hook 触发 deopt 即可。

六、招商银行:3 套独立 SDK 并联

目标:cmb.pb 13.2.0。症状:启动后两个不同弹窗——CmbShield Toast「检测到您当前设备可能存在风险」+ 后续 AlertDialog「安全风险」+ Activity「注入风险」。

3 套独立反检测 SDK 并联,任意一套命中都弹窗,必须全部伪装:

SDK文件检测内容触发 UI
CmbShieldlibCmbShield.so 自研frida 自检测 + maps 扫描 + anti-ptrace + dl_iterate_phdr + root 文件扫描写信号文件 → AW.hanDeRe → Toast Cts.RTM
RootBeer + SecBaseFuncdex(com.scottyab.rootbeer 改装)+ libtoolChecker.so7 项 root 检测(包名 / File.exists / Runtime.exec which su / Build / sysprop)CheckRootTask → AlertDialog「安全风险」
DeviceEnvironmentCheck动态加载 dex(com.a.b.c.*,不在 base.apk 内)周期轮询读 envc.push 文件 0/1CheckTask.onPostExecuteDECMsgDialog Activity「注入风险」

非加固——12 个 dex 是标准 multidex,没有 libjiagu/libshell/libDexHelper,常规脱壳工具链在这没东西脱。开发把火力放在 3 套并联 + native 检测 + 动态加载 dex 上。下面三节逐套讲检测点和绕过。

6.1 CmbShield 自研防护层

入口三件套(Manifest):

<application
    android:appComponentFactory="cmb.shield.AP"    <!-- Android 9+  Application  -->
    android:name="cmb.shield.AW"                   <!-- 替身 Application,hanDeRe 弹窗判定中心 -->
    ... />

cmb.shield.APcmb.shield.AW.attachBaseContextH.load() → System.loadLibrary("CmbShield")JNI_OnLoad

JNI_OnLoad 里启动一组 native 检测异步线程:

检测点做什么命中后
parse_self_maps/proc/self/mapsfrida-agent / linjector / gum / re.frida 字符串写信号文件 Cts.A
anti_ptrace(run_secsdk)ptrace(PTRACE_TRACEME) 自己 ptrace 自己,让真调试器 attach 失败
TracerPid 双进程fork app_process 子进程读自身 /proc/<ppid>/status 的 TracerPid,绕过自 ptrace 后仍能暴露调试器写信号文件
dl_iterate_phdrlinker 内部 SO 枚举(绕过 maps),过滤 dlpi_name 含 frida 的 entry写信号文件
check_sd_root_files/system/bin/su 等 root 路径写信号文件 Cts.B
kill_injecthook android_dlopen_ext,对 frida-agent.so 主动破坏直接让 frida-agent 注入失败

延迟反馈机制:native 命中后不直接弹窗,而是通过 H.sha(key, path) JNI 接口把一个信号文件路径写到静态变量 Cts.A / Cts.B。Java 端 super.onCreate() 才到弹窗判定中心 hanDeRe()

private void hanDeRe() throws Exception {
    if (isMp()) {
        boolean fDted = dRbf(Cts.B);   // File.exists(Cts.B) ? delete + true : false
        boolean xDted = dRbf(Cts.A);
        if (fDted || xDted) {
            Toast.makeText(this, Cts.RTM1).show();  // "检测到您当前设备可能存在风险..."
        }
        if (!fDted) startService(ShieldService.class);  // 30s 周期再扫
    }
}

三处精巧设计:

  • Toast 调用栈完全干净——栈里只有 hanDeRe → dRbf → File.exists → Toast,看不到任何 native 检测痕迹。第 5 章酷狗案例的 Toast 反查工作流在这里失效
  • 删后即焚——dRbf 触发后立即 delete(),逆向者 adb shell 进去看的时候文件早没了
  • 服务器侧风控上报——Clc.scl 异步 POST 到 Cts.G绕过本机但绕不过账号标记

绕过主路径 —— 掐弹窗判定中心 + 断上报:

// Tier 1 — 弹窗判定中心 noop
const AW = Java.use('cmb.shield.AW');
AW.dRbf.implementation     = (path) => false;
AW.hanDeRe.implementation  = function () {};

// Tier 2 — 必须配合 Tier 1,否则账号被服务器侧打标
const Clc = Java.use('cmb.shield.Clc');
Clc.scl.implementation     = function () {};
Clc.uclbn.implementation   = function () {};

Tier 1 灭弹窗,Tier 2 灭上报。Toast 没了不等于绕过——同一份内部状态会被服务器读到,必须用 mitmproxy 抓 Cts.G 域名确认上报真的被断了。

TracerPid 双进程检测的对抗:第 18 篇 fgets 拦 TracerPid 行只覆盖当前进程;子进程要么 hook fork / posix_spawn 之后在子进程也注入脚本(frida 17 的 gating 模式支持),要么 native 层 hook 子进程的 open("/proc/.../status")。详见第 18 篇反调试。

6.2 RootBeer 7 个子检测 + 两个易漏点

RootBeer 是 com.scottyab 的开源 root 检测库,招行用它的改装版(外加 SecBaseFunc.checkRootOld File.exists 兜底)。

Dialog #1 "安全风险" 弹窗
 ↑ BaseAlertDialog.show
 ↑ CheckRootTask$a.onSuccess
 ↑ SecBaseFunc.isRoot()
     ├─ RootBeer.isRooted()        ← OR 7 个子检测
     └─ SecBaseFunc.checkRootOld() ← File.exists() 直接扫

7 个子检测:

方法检测原理
detectRootManagementAppsPackageManager.getPackageInfo 扫 magisk/supersu 包名
checkForBinary("su" / "busybox" / "magisk")File.exists 扫 16 条 root 路径
checkForDangerousProps__system_property_getro.debuggable / ro.secure
checkForRWPathsFile.canWrite("/system", "/data", ...)
detectTestKeysBuild.TAGS.equals("test-keys")
checkSuExistsRuntime.exec(new String[]{"which","su"}) ★ 易漏
checkForRootNativeJNI → libtoolChecker.sofopen(path,"r") ★ 易漏

前 5 项第 2.1 节 / 第 2.3 节 / 第 2.4 节的 hook 直接覆盖。两个易漏点必须特别处理:

6.2.1 checkSuExists 用数组形式

public boolean checkSuExists() {
    Process p = Runtime.getRuntime().exec(new String[]{"which""su"});  // ★ 数组!
    return new BufferedReader(new InputStreamReader(p.getInputStream())).readLine() != null;
}

部分按"只检查 cmds[0]"实现的 hook 会漏——cmds[0] = "which" 不在 su 黑名单。正确做法:对整个 String[] 数组里任一元素su / busybox / magisk 都拦。本文第 2.2 节的 hook 是先拼接数组成字符串再 indexOf,所以已经覆盖;写新 hook 时要留意这个坑。

6.2.2 checkForRootNativefopen 而非 access/stat

// libtoolChecker.so 内部
bool exists(const char *path) {
    FILE *= fopen(path, "r");      // ★ fopen,不是 stat/access!
    if (f) { fclose(f); return true; }
    return false;
}

朴素 native hook(只覆盖 stat/lstat/access/faccessat)漏。第 3 章主代码块的 ④ fopen 拦截已经覆盖——这是为什么第 3 章必须把 fopen 加进 isRootPath 通配

完整伪装验证(应用全套 hook 后跑 research2.js):

RootBeer.checkSuExists()        = false  ✓  ← Runtime.exec 数组覆盖
RootBeer.checkForRootNative()   = false  ✓  ← fopen 拦截
RootBeer.checkForMagiskBinary() = false  ✓
RootBeer.isRooted()             = false  ✓✓✓ (招行自己判定环境干净)

Magisk 也不再弹"超级用户请求"——因为 Runtime.exec / native execve 都拦在源头,没真去申请 su。

6.3 DeviceEnvironmentCheck:动态加载第三方 SDK

完整解决 RootBeer 后 App 仍弹「注入风险」——第三套独立 SDK:

Activity DECMsgDialog "注入风险"
 ↑ BaseFragmentActivity.startActivityForResult
 ↑ DeviceEnvironmentCheck$MsgDialog.show
 ↑ DeviceEnvironmentCheck.showMessage
 ↑ DeviceEnvironmentCheck$CheckTask.onPostExecute
 ↑ DeviceEnvironmentCheck$CheckTask$1$1.run(周期轮询)

包名混淆为 com.a.b.c.*,dex 不在 base.apk 任何位置(binary grep + dexdump -l plain 全 0 命中),是运行时动态加载的。

ClassLoader 突破Java.use('com.a.b.c.DeviceEnvironmentCheck') 在主 factory 失败,要枚举所有 loader 找能 findClass 的:

let loader = null;
Java.enumerateClassLoadersSync().forEach(l => {
    if (loader) return;
    try { if (l.findClass('com.a.b.c.DeviceEnvironmentCheck')) loader = l; } catch (_) {}
});
const factory = Java.ClassFactory.get(loader);
const DEC = factory.use('com.a.b.c.DeviceEnvironmentCheck');

检测机制AsyncTask.doInBackground 周期循环(LOOP_TIME 间隔,最长 MAX_TIME)调 getCheckResult(key),返回 0/1。getCheckResult 内部只做一件事——读 /data/user/0/cmb.pb/envc.push 文件

ENTER getCheckResult("frida")
  IO open ("/data/user/0/cmb.pb/envc.push")
  IO fopen("/data/user/0/cmb.pb/envc.push")
EXIT  getCheckResult("frida") = 1

envc.push 是 25 字节文件(24 char Base64 + \n),解码后 16 字节正好 1 个 AES-128 block 密文,每次启动重新生成(pm clear 后),AES key 是随机 session key:

$ adb shell su -c "cat /data/user/0/cmb.pb/envc.push"
s8RY6BbWj1lfdgQxncxNmQ==

envc.push 未解之谜:hook 了所有 libc 写 API(open/openat/openat2/creat/__open_2/fopen/write/fwrite/fputs/pwrite/rename/truncate 等近 20 个),全部 0 命中。但时序证据显示 envc.push 在某次 doInBackground EXIT 之后突然存在了。推测写者用了 raw inline syscallmov x8, #__NR_openat; svc #0)绕过 libc 函数层,定位需 Stalker 或指令级 trace,留作扩展。

绕过路径:不需要破解 AES,从 Java 层最高点拦:

// Tier A: getCheckResult 永远返 0(推荐,最简)
DEC.getCheckResult.implementation = function (key) { return 0; };

// Tier B: Support 黑名单全空(额外保险)
const Support = factory.use('com.a.b.c.DeviceEnvironmentCheck$Support');
Support.isSupport.implementation     = function (k) { return false; };
Support.isWarn.implementation        = function (k) { return false; };
Support.isWarnAndExit.implementation = function (k) { return false; };

// Tier C: saveInjectInfo noop(阻止持久化命中事件)
const CheckTask = factory.use('com.a.b.c.DeviceEnvironmentCheck$CheckTask');
CheckTask.saveInjectInfo.implementation = function () {};

A+B+C 三层组合后,App 主界面完全没有任何风险弹窗,Magisk 也不再弹"超级用户请求被拒绝"。

6.4 环境伪装 vs UI 兜底

三套 SDK 都伪装完之后,回头看整篇思路。脚本采用的是环境伪装——hook 检测 API 改输入数据,让 App 跑完所有检测但读到的环境数据全是干净的,App 真的判定自己处于干净环境。

环境伪装的最小完备 API 覆盖清单——招行实战验证:

Native 层(libc / libdl):

fopen                                  ← maps 扫描 / fopen 探 root(招行核心点)
fread                                  ← 配合 fopen 读 maps,stream 内 chunk 字串替换
stat / lstat / access / faccessat      ← 探 root 文件
execve / execv / execvp / posix_spawn  ← native fork+exec su
system / popen                         ← shell 命令
__system_property_get                  ← 读 sysprop
dl_iterate_phdr                        ← linker 内部 SO 枚举,wrap callback 过滤 dlpi_name 含 frida

Java 层:

Runtime.exec 所有 4 个重载              ← 数组形式必须查任一元素
ProcessBuilder.start                   ← command list 含 su 类
File.exists / canRead / canExecute     ← root 路径
Build.TAGS / Build.FINGERPRINT         ← test-keys 替换
ApplicationPackageManager.getPackageInfo / getApplicationInfo
System.getenv("PATH")                  ← 过滤 sbin/xbin

DEC SDK(专用 ClassFactory):

getCheckResult(key)         → 永远 0
Support.isSupport/Warn/... → false
CheckTask.saveInjectInfo → noop

第 2、3 章主代码块已经覆盖大部分。招行特有的两条扩展全文已经吸收:① 第 2.2 节 Runtime.exec 拼接后 indexOf 已覆盖数组任一元素;② 第 3 章 Native 主代码块的 ④ 已含 fopen

6.5 启动方式 + 必备配置

spawn 启动——检测在 attachBaseContext 就跑,attach 晚一拍全晚:

frida -U -f cmb.pb -l cmb_bypass.js --no-pause

设备配置:

  • Zygisk + DenyList 把 cmb.pb 加进去——避免 RootBeer 早早命中
  • mitmproxy 抓 Cts.G 域名验证 Tier 2 上报真的被断了
  • 长时间运行还要处理周期检测:CmbShield 的 ShieldService(30s 一次)和 DEC 的 CheckTaskLOOP_TIME 间隔轮询,最长 MAX_TIME

总结

四个维度的 Hook 点速查(已同步 第 2、3 章 全部点):

维度关键证据着力点
文件su / Magisk / KernelSU / SuperSU 文件Java:File.exists + canExecute + canRead 三件套;Native:access/faccessat + stat/lstat/fstatat + open/openat + fopen 共 10+ 函数。路径用 endsWith("/su") 通配
Shellwhich su / su -c id / mountRuntime.exec 所有重载拦截
系统属性ro.build.tags=test-keys / ro.debuggable=1 / verified bootSystemProperties.get 双重载 + Build.TAGS / Build.TYPE 静态字段反射
包名Magisk Manager / SuperSU / Xposed 包名getPackageInfo × 2 + getInstalledPackages × 2 + getInstalledApplications × 2,共 6 入口

实战要点:

  1. Native 先于 Java + spawn——JNI_OnLoad 早于 Java 检测线程,招行 6 个检测都在这里启动,attach 来不及。每段 Hook 用 try/catch 兜,打失败 .message 方便下次定位。
  2. 路径通配 > 白名单——endsWith("/su") || indexOf("magisk") 比 20 条路径列表稳。酷狗 Q6()$PATH,命中 /system_ext/bin/su(Magisk systemless 常见位置),老清单挡不住。
  3. 加固 SDK 找输出口——第 4.3 节 四步法。grep 第一个候选可能是迷雾(酷狗 FireEye),用 第 5 章 Toast / Dialog 反查验证。Hook 装上没报错但收不到事件,多半是 ART AOT inline——给持有它的 Activity 方法挂个 no-op hook 触发 deopt(详见 第 5.2 节)。
  4. Toast 栈干净 ≠ 没检测——招行延迟反馈:native 命中只写信号文件,Java onCreateFile.exists 弹窗。搜常量 / 信号文件路径的 caller(第 6.3 节 ①),或 hook 全部 new File().exists() 看哪些路径被访问。

📦 获取本篇脚本

本篇整套 Root 检测绕过脚本(Java 四维 Hook + Native 层 libc Hook + 酷狗 / 招行双实战绕过,9 个脚本 + RUN.md 真机验证清单,复制即用)已打包:

  1. 关注本公众号
  2. 私信回复关键词「脚本」

回复内含本系列与其它系列(Unidbg / SO 逆向 / ARM 汇编 ……)的脚本汇总,长期维护更新。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-05-31 03:07:31 HTTP/2.0 GET : https://67808.cn/a/491740.html
  2. 运行时间 : 0.091897s [ 吞吐率:10.88req/s ] 内存消耗:4,626.19kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=f8646887d865f2d4a2c897850991c6b8
  1. /yingpanguazai/ssd/ssd1/www/no.67808.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/no.67808.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/no.67808.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/no.67808.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/no.67808.cn/runtime/temp/6df755f970a38e704c5414acbc6e8bcd.php ( 12.06 KB )
  140. /yingpanguazai/ssd/ssd1/www/no.67808.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000584s ] mysql:host=127.0.0.1;port=3306;dbname=no_67808;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000916s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000368s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000280s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000489s ]
  6. SELECT * FROM `set` [ RunTime:0.000220s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000612s ]
  8. SELECT * FROM `article` WHERE `id` = 491740 LIMIT 1 [ RunTime:0.003513s ]
  9. UPDATE `article` SET `lasttime` = 1780168051 WHERE `id` = 491740 [ RunTime:0.005030s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 65 LIMIT 1 [ RunTime:0.000245s ]
  11. SELECT * FROM `article` WHERE `id` < 491740 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000460s ]
  12. SELECT * FROM `article` WHERE `id` > 491740 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000393s ]
  13. SELECT * FROM `article` WHERE `id` < 491740 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000739s ]
  14. SELECT * FROM `article` WHERE `id` < 491740 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.003843s ]
  15. SELECT * FROM `article` WHERE `id` < 491740 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.007328s ]
0.093640s