
文章篇幅有限,项目开源地址:taisuii/sentry: frida检测
项目本身不是什么新东西,算是把一些检测什么的综合一下吧
这里抛出一个问题:大家觉得APP的风控中,检测到用户在注入和调试风控系数高,还是手机越狱或者root环境系数高呢?
显然是从注入和调试风控系数高,因为有很多用户的手机可能是二手或者是搞基佬,不一定root的设备或者解了BL的就是黑灰产,反而喜欢调试和注入的用来RPC的,更容易被评定为灰产机器
这里主要是强调尽量使用syscall,以及多重验证,形成证据链去风控
敏感检测下沉到 Native,有利于对抗「只改 Java 层」类绕过。
syscall → libc 回退:在受限机型上仍能读数据,兼顾对抗强度与兼容性。
syscall 优先(读 /proc、套接字等):减少对 libc 常规导出符号的依赖,降低 Frida/Xposed 一刀切 Hook libc 导致整类检测失效的概率。
不把结论绑在单一 API 上,例如:
Maps:Native 解析 + Java Runtime.exec("cat /proc/.../maps"),对抗只 patch 一条读路径。
ADB:Native(端口、/proc/net/tcp、adbd、sysfs)+ Java Settings + getprop/settings get 等 exec 兜底。
Bootloader:系统属性 + Key Attestation(RootOfTrust 等)组合。
Xposed/Hook:Java(类、堆栈、反射、ClassLoader)与 Native(路径/fd、inline/PLT·GOT、匿名 r-x、ARM64 LR 等)组合。
端口,字符串特征,内存
如果单独对这些字符串做检测,依然可能会被魔改的frida绕过,那么还有一些较强的
组合 libc .text 与磁盘 CRC、GOT 劫持、27042 端口预检、匿名可疑 r-x、关键函数首部 Frida 典型 inline 模式 等,native-lib.cpp 里汇总为一条「Frida/SO 异常」类结论。
不是单纯 CRC:还叠了 GOT、可疑匿名可执行段、关键函数头模式、端口预检等,能单列成“组合完整性检测”小节(so_integrity.cpp、native-lib.cpp)
看 Activity.onCreate 等方法的 entry_point 是否落在由 maps 汇总的「合法可执行区」外;典型 Frida Java Hook 常把入口指到 libart/oat 外的可执行岛。
系统库(如 libc.so)的代码段一般是文件映射、多进程只读共享。
若用 inline hook 之类方式改机器码,内核会对这一页做 COW(写时复制):本进程拿到一份私有可写副本,改的是「自己的那份」。
这类页在统计上常表现为:
/proc/self/smaps 里该映射区段的 Private_Dirty 大于 0(有私有且被改过的脏数据)。
某些情况下还可配合 /proc/self/pagemap 里与「近期是否被写过」相关的 soft-dirty(位 55) 看是否像发生过写入/COW 链路。
因此:「只读共享的正版代码段」长期出现明显私有脏页,和「有人动过这块代码」在经验上高度相关;项目把它用在 Frida 全局 hook / Zygisk 类注入 等会动到 libc/libart 等场景上。
原理:逐行读 smaps,先根据映射行判断是否落在 r-xp / r-x(可执行) 区,再在同一区段后续的统计行里找 Private_Dirty:。若 dirty_kb > 0,且映射落在可疑路径(路径里带 .so 或 libart/libc/libselinux/libandroid_runtime 等),且不在白名单(如 code_cache、libstagefright.so),就记一条告警。
注意:smaps 是内核给出的会计信息,不是应用自己瞎算;但仍需白名单压误报(JIT、多媒体库等也可能在可执行映射上出现非零 Private_Dirty)。
原理:对给定虚拟地址,算出所在页号,在 pagemap 里读 8 字节项,检查:
bit 63:页是否在物理上 present(项目里叫 PAGEMAP_BIT_PRESENT)。
bit 55:soft-dirty(项目里注释写明用于「COW/曾修改」类指纹)。
代码注释里强调:不要用错 bit(例如注释里说 bit 61 对文件映射的 libc 会恒为 1,误用会误报),所以实现里显式用的是 bit 55。
「精准」用法:不是全进程扫 pagemap,而是用 dlsym 取 libc 里 fork / vfork / signal 的地址,只查这几个常被 Frida/注入链 hook 的入口所在页:
严格说 VMap 那一段不是脏页检测:它在 /proc/self/maps 里找「匿名 + 可执行」段,再在内存里 memmem 搜 Zygisk 特征串。它和 smaps / pagemap 并列,同属 env_detect_zygisk_injection,所以里常写成 「Smaps 脏页 + VMap + Pagemap」。
边界:脏页类检测会受 ROM/内核版本、厂商、调试开关、合法 JIT/code_cache 等影响,所以实现里用 白名单 + 限定关键库/关键符号,并和 maps 特征一起做,而不是单一指标定罪。
比如:自定义 handler + 发信号验证信号链路是否被劫持Ptrace/附加检测:TracerPid + PTRACE_TRACEME
反射关键方法探测、ClassLoader 异常实例检测(LSPosed/InMemoryClassLoader 等)
这一层适合「低成本扫一眼」,不能当最终结论。
特点:看「常见特征」:装没装、路径在不在、Java API 读到的包名。对抗方常用隐藏应用、卸载管理端、卸载列表、挂载/命名空间藏目录、或 Hook PackageManager / File / access
对已安装包做 getInstalledPackages(PackageManager.GET_META_DATA),看 ApplicationInfo.metaData 里是否声明 Xposed 模块常见键:
用 open_with_fallback / read_with_fallback(先 syscall 再 libc)读 cgroup,匹配 lxc / docker / kubepods 等,对应云手机、K8s Pod 一类环境,和传统 Root 检测互补。
这个检测,走硬件信任根,不能像改文件一样随便伪造;限制在设备/ROM/谷歌服务/证书链是否可用、以及服务端是否校验(你们侧主要是本地解析)
detectBootloader() 在 Native 读 AVB/Verified Boot 相关系统属性(如 verifiedbootstate、vbmeta.device_state、veritymode 等)之外
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!