某校园跑简单的反作弊分析
该校园跑软件反作弊的手段主要有两个:对于跑步过程进行人脸校验、对手机的环境进行检测。
我们聚焦于 “对手机的环境进行检测” 这一方面,此前的咕咚跑并未对此有严格的检测,所以使用模拟定位等一些列手段就可以轻松实现跑步作弊。
而该校园跑软件对于手机的环境极其严格,并且对软件进行了加固,以至于不能直接拿到源代码(未加密的 dex),但是通过某种手段,我成功 dump 出了该校园跑的 dex 文件。
首先该校园跑软件在 Native 层和 Java 层均有做检测,当然这仅限于安卓的环境,对于 IOS 暂时不知道具体的检测内容(IOS可能好作弊一点)。
下面将会介绍每个层的检测技术以及校园跑过程中到底会提交什么数据。
Native 层
Native 层简单来说是已经编译好的动态库文件,已经编译为纯二进制文件了,使用 IDA 反编译查看伪代码之后,发现了如下的检测手段:
Hook 检测
hook 的意思是 “钩子” 的意思,使用 hook 技术可以动态修改程序运行时候函数的返回值,以便控制程序的代码走向(比如伪造数据等等、跳过验证等等),
正是如此,软件需要检测 hook ,不允许其他程序轻易修改自己的代码执行逻辑。
- 进程列表扫描:扫描 xposed、frida 等关键字,判断是否有 Hook 进程。
- 内存映射扫描:扫描 XposedBridge.jar、com.saurik.substrate、frida-agent.so,判断是否 Hook 注入。
- 命令行参数读取:自身命令行是否包含异常参数(识别动态注入痕迹)
模拟器检测
如果软件运行在模拟器中,仍然可以通过其他手段修改定位、遥感数据等等内容,所以程序会检测模拟器。
- 设备文件检测:检测 VirtualBox、QEMU、Windroye、MuMu、夜神、逍遥等模拟器特征文件。
- CPU 信息检测:goldfish、ranchu、vbox、intel 等 CPU 特征
- 系统属性检测:品牌、型号、硬件平台,识别模拟器常见的值(如 google_sdk、vbox86)
自检测
判断自己的 app 是否被修改。
- 反调试与完整性校验:自身是否被调试器附加,防止动态分析
- 签名校验:查看自身是否被篡改
- 文件完整性校验:libInno.so、libInnoSecure.so 等是否被修改,防止 Hook 框架修改 SO 文件
其他
剩下的就是增加逆向难度的一些修改:
- RC4 加密校验:验证上报数据的完整性或服务器返回的数据是否合法
- 字符串混淆与加密:敏感字符串(如文件路径、属性名)在 Native 层动态解密,增加逆向难度
最后,如果上述有一个条件检测不通过,就调用 exit 主动退出程序。
Java 层
上述,Native 层的检测都是软件一打开就开始检测的,如果不对执行的是关闭程序(即使你没有登录校园跑账号、没跑步也会触发),
虽然上述的检测方式多种多样,但是最终目的是关闭程序,所以理论上只要拦截程序的关闭逻辑就可以绕过继续使用软件。(因为退出时,程序实际上已经加载完成。)
所以,该校园跑设计了 Java 层的检测逻辑,猜测这些检测部分是在跑步时或者使用的时候动态检测的(因为是技术分析,并没有真用于作弊)。
Hook 检测
对于检测是否 Hook ,就使用了 13 种方法。
- maps 文件扫描:读取
/proc/self/maps,查找XposedBridge.jar、com.saurik.substrate等特征字符串 - 异常堆栈分析:主动抛出异常,遍历堆栈查找
xposed、XposedBridge、Substrate、ZygoteInit(出现两次)等特征 - Xposed 内部 Map 反射:反射获取
XposedBridge.sHookedMethodCallbacks,若不为空则判定存在 Hook - XposedHelpers 缓存检测:反射获取
XposedHelpers.methodCache,检测是否有缓存的 Hook 方法 - Xposed 类加载检测:尝试加载
de.robv.android.xposed.XposedBridge,若成功则判定存在 - EdXposed 检测:尝试加载
com.elderdrivers.riru.edxp.config.EdXpConfigGlobal - VirtualXposed 检测:读取
System.getProperty("vxp"),若不为空则判定存在 - CLASSPATH 环境变量检测:读取
System.getenv("CLASSPATH"),检查是否包含XposedBridge - Xposed 主动禁用尝试:尝试修改
XposedBridge.disableHooks字段为true,若成功则证明 Xposed 存在 - Native 方法状态检测:判断
Throwable.getStackTrace()等方法是否仍为 native 方法,若被 Hook 则可能变为非 native - Substrate 检测:堆栈中查找
com.saurik.substrate.MS$2.invoked方法 - Frida 进程检测:通过进程列表扫描
frida关键字(Java 层调用 shell 命令) - 应用列表扫描:获取已安装应用列表,查找是否包含
de.robv.android.xposed.installer等 Xposed 安装器
模拟器检测
- Build.HARDWARE 检测:读取硬件名称,检测是否包含
goldfish、vbox86、nox、ttvm、vbox、intel、android_x86等模拟器特征 - Build.PRODUCT 检测:读取产品名称,检测是否为
sdk、google_sdk、sdk_x86、vbox86p、nox等 - Build.MODEL 检测:读取设备型号,检测是否包含
google_sdk、emulator、android sdk built for x86、droid4x、sdk - Build.MANUFACTURER 检测:读取制造商,检测是否为
genymotion、netease(网易 MuMu) - Build.FINGERPRINT 检测:读取指纹,检测是否以
generic开头 - Build.BOARD 检测:读取主板信息,检测是否为
goldfish、nox、unknown - Build.BOOTLOADER 检测:读取引导程序版本,检测是否包含
nox - Build.BRAND + Build.DEVICE 组合检测:检测品牌和设备是否都以
generic开头 - Build.TAGS 检测:检测标签是否为
test-keys(模拟器常见) - ro.build.flavor 属性检测:读取系统属性,检测是否包含
vbox、sdk_gphone - ro.board.platform 属性检测:读取平台信息,检测是否包含
android(x86 模拟器特征) - gsm.version.baseband 基带检测:读取基带版本,检测是否为
1.0.0.0(模拟器常见) - CPU 信息检测:执行
cat /proc/cpuinfo,检测是否包含intel或amd(x86 架构) - CPU 架构检测:判断 CPU 是否为 x86 架构(真实手机多为 ARM)
- 特征文件检测:检查是否存在模拟器特有文件(BlueStacks、夜神、MuMu、天天、海马玩等 60+ 个特征路径)
- QEMU 设备文件检测:检查
/dev/socket/qemud、/dev/qemu_pipe是否存在 - Goldfish 设备检测:检查
/proc/tty/drivers、/proc/cpuinfo是否包含goldfish关键字 - CGroup 信息检测:读取
/proc/self/cgroup,分析容器/虚拟化特征 - 系统功能检测:检查设备是否具有真实硬件功能(闪光灯、摄像头、蓝牙),模拟器常缺失
- CPU 核心数检测:读取
/sys/devices/system/cpu/possible,模拟器核心数与真实设备可能有差异 - CPU 频率检测:读取
/sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_max_freq,模拟器频率特征异常 - SD卡 CID 检测:读取
/sys/block/mmcblk0/device/cid,模拟器返回值异常 - Boot ID 检测:读取
/proc/sys/kernel/random/boot_id,模拟器可能固定或缺失 - UUID 检测:读取
/proc/sys/kernel/random/uuid,模拟器可能格式异常 - 内存信息检测:读取
/proc/meminfo,模拟器内存大小可能与真实设备不符 - 屏幕分辨率检测:获取屏幕分辨率,模拟器常使用标准分辨率(如 1080×1920)但比例可能异常
- 电池状态检测:注册电池广播,模拟器电池状态可能为固定值(如一直充电中)
- 音频音量检测:读取各音频流音量,模拟器可能有默认值或缺失
- 已安装应用列表检测:扫描已安装应用,查找模拟器特有应用(如 BlueStacks 组件)
Root 检测
是否可以拿到手机的最高权限(一般手机是拿不到这个权限的)
- test-keys 检测:读取
Build.TAGS,检测是否包含test-keys(用户调试签名) - su 文件检测:检查
/system/bin/su、/system/xbin/su文件是否存在 - Superuser.apk 检测:检查
/system/app/Superuser.apk文件是否存在 - Magisk 应用检测:扫描已安装应用列表,查找
com.topjohnwu.magisk等 Magisk 管理器 - SuperSU 应用检测:扫描已安装应用列表,查找
eu.chainfire.supersu等 Root 管理工具 - KingRoot 应用检测:扫描已安装应用列表,查找
com.kingroot.kinguser等 Root 工具 - Root 相关进程检测:执行
ps命令,查找su、magisk、daemonsu等进程 - 系统分区可写检测:尝试写入
/system分区,判断是否为只读(Root 设备通常可写) which su命令检测:执行which su命令,检测是否能找到 su 路径id命令检测:执行id命令,检测当前用户是否为uid=0(root)- 安全属性检测:读取
ro.debuggable、ro.secure等系统属性,判断是否可调试 - busybox 检测:检查
/system/bin/busybox、/system/xbin/busybox是否存在 - Root 管理工具包名扫描:扫描已安装应用,检测包名是否包含
root、supersu、superuser、magisk等关键字 /data目录权限检测:检查/data目录是否可读可写(Root 设备常开放权限)/system/bin/fakeroot检测:检查是否存在假 Root 工具文件/system/bin/daemonsu检测:检查 SuperSU 守护进程是否存在/sbin/su检测:检查sbin目录下的 su 文件(Magisk 常在此处)/vendor/bin/su检测:检查 vendor 分区下的 su 文件- Root 隐藏检测:通过多种路径组合检测(
/data/local/xbin/su、/data/local/bin/su等) - Symbolic Link 检测:检查 su 文件是否为符号链接(Root 隐藏常用手法)
- 环境变量 PATH 检测:遍历 PATH 中的所有目录,检查是否存在 su 文件
mount命令检测:执行mount命令,检测/system分区挂载选项是否包含rw(可读写)
上报的数据
实时上报数据(跑步过程中)
分段配速数据
- 每公里/每段的配速值
- 上报间隔(如每1公里)
- 用于反作弊分析,检测配速是否异常
分段步频数据
- 每段距离的步频值
- 步数统计
- 用于验证跑步连贯性,检测是否作弊
分段步幅数据
- 步幅变化曲线
- 结合步频计算更准确的速度
GPS 轨迹点
- 完整的经纬度坐标列表
- 时间戳
- 经过 AES 加密 + Gzip 压缩
- 用于路线验证、距离真实性校验
打卡点状态
- 各打卡点的通过状态
- 通过时间戳
- 打卡顺序
- 用于验证是否按规定路线跑步
人脸识别数据
- 人脸照片(压缩后上传 OSS)
- 人脸校验状态
- 比对结果
- 用于身份验证,防止代跑
跑步结束后上报数据
基础运动数据
- 总距离(米)
- 有效距离(米)
- 总时长(秒)
- 消耗卡路里
- 平均配速
步频与步数
- 总步数
- 平均步频
时间与标识
- 开始/结束时间戳
- 跑步记录码(唯一标识)
- 运动类型(阳光跑/自由跑)
- 运动场景(视觉识别/人脸识别/刷卡机)
环境检测数据
- 是否模拟器(
iss) - 是否 Root(
isr) - 是否 Hook(
ish) - 设备指纹信息(
cuidSour、acidSour) - USB 调试状态(
isou)
人脸识别结果
- 人脸校验状态(通过/失败/未完成)
- 用户人脸照片 URL
- 多张人脸比对照片 URL 列表
- 人工复核状态
设备硬件信息
- 屏幕分辨率
- 内存大小
- CPU 信息
- 电池状态
- 音频音量配置
上述的数据难以直接伪造,如果使用模拟定位软件,是很难模拟 分段步幅数据 等传感器数据的,虽然技术上可行,但是非常困难。
总结
通过软件直接模拟跑步非常困难,而且难以确保每个环境检测都可以通过,所以相比之下,成本较低的作弊方式还是接力跑(狗头)。
当然不是鼓励大家作弊,这是一篇简单的技术分析,以示大家不要相信网上的校园跑科技代刷,目前来看,完美代刷是非常难实现的!大家还是自己慢慢跑好了~










