
简介这是一套面向 iOS 17 及以上系统的免越狱虚拟定位跑步打卡工具适合希望在无法外出时模拟运动轨迹、完成跑步类应用打卡的用户也便于开发者研究跨平台定位模拟的实现思路。资源包共 16 个文件以 9 个 Python 脚本为核心配合 yml、yaml 配置、txt 路线数据、md 说明文档及 license 等整体约 15KB结构轻量、便于阅读与二次修改。目前已有 703 人学习下载。代码围绕主入口、设备连接、定位驱动、路径拾取与隧道通信等模块展开支持在线拾取真实路线并自定义跑步距离、时间与速度等参数读者可据此理解虚拟定位与轨迹模拟的完整链路并参考其模块划分与配置方式搭建自己的实验环境。需注意此类技术可能违反部分运动应用的使用条款请理性评估风险后再使用。1. 免越狱模拟跑步打卡从“物理外挂”到跨平台工具链的落地拆解如果你手里有一台 iOS 17 以上的设备又不想折腾越狱却需要让跑步类 App 记录一段“看起来合理”的轨迹那这套基于虚拟定位的模拟跑步方案就是为你准备的。它解决的核心问题很具体在不破坏系统签名的前提下把系统级定位输出替换成一条可编程的路线让 App 读到连续的经纬度、速度和时间戳。适合两类人一是需要反复测试跑步类 App 轨迹逻辑的开发者二是想研究 iOS 定位链路的技术爱好者。整套工具跨平台运行Windows、macOS、Linux 都能跑iOS 17 之后的版本对开发者模式的要求更严这也是为什么很多老方案在 17 上直接翻车。下面我把这套东西从原理到参数到坑按能复现的顺序拆一遍。2. 定位模拟的底层链路从开发者模式到虚拟坐标注入2.1 为什么 iOS 17 之后必须走开发者模式iOS 17 对定位服务的校验比 16 严了一个量级。以前用一些第三方工具直接改系统缓存就能生效现在系统会校验定位来源的签名链。常见做法是走 Xcode 的开发者模式把设备标记为开发机然后用devicectl或者配套的跨平台工具建立调试通道。这个通道本身是苹果给开发者调试定位用的所以不需要越狱也不需要企业证书。关键点在于设备必须开启“开发者模式”开关这个开关在设置-隐私与安全性里插上电脑后才会出现。很多人卡在这一步以为工具坏了其实是开关没打开。2.2 虚拟坐标注入的三种常见实现路径目前能跑通 iOS 17 的路径主要有三类。第一类是直接用 Xcode 的模拟定位功能但只能选预设城市不能自定义路线适合快速验证。第二类是通过pymobiledevice3这类开源库建立 lockdown 连接再调用dvt服务注入坐标跨平台支持最好Windows 上也能跑。第三类是用现成的 GUI 工具底层也是走 dvt但封装了路线编辑和速度曲线。我一般会选第二类因为脚本可控参数能落盘出问题能看日志。下面这段是建立连接并注入单个坐标的最小示例# 依赖 pymobiledevice3安装pip install pymobiledevice3 from pymobiledevice3.lockdown import create_using_usbmux from pymobiledevice3.services.dvt.instruments.dvt_provider import DvtProvider from pymobiledevice3.services.dvt.instruments.location_simulation import LocationSimulation async def inject_point(lat, lon): # 通过 usbmux 找到设备不依赖 iTunes lockdown await create_using_usbmux() # DvtProvider 是 iOS 17 上唯一稳定的注入通道 async with DvtProvider(lockdown) as dvt: async with LocationSimulation(dvt) as sim: # set 方法接受纬度和经度单位是十进制度 await sim.set(lat, lon) print(finjected: {lat}, {lon})逻辑说明create_using_usbmux走的是 USB 多路复用协议不需要额外装驱动。DvtProvider负责建立调试会话LocationSimulation是具体注入点。参数lat和lon是浮点数范围分别是 -90 到 90 和 -180 到 180。注意每次set之后坐标会立即生效但如果你连续调用太快系统会丢包建议间隔 0.5 秒以上。2.3 跑步轨迹的插值逻辑为什么不能只给起点和终点跑步 App 读的不只是坐标还有速度、步频和时间戳。如果你只注入起点和终点App 会算出瞬移直接判定异常。常见做法是在两点之间做线性插值按固定时间间隔生成中间点。比如你要模拟 5 公里跑配速 6 分钟每公里那总时长 30 分钟按每秒一个点就是 1800 个坐标。插值公式很简单lat lat1 (lat2 - lat1) * tlon同理。但要注意地球曲率短距离内线性插值够用超过 10 公里最好用球面插值。下面是一个生成轨迹点的片段import time def generate_route(start, end, duration_sec, interval1.0): # start/end 是 (lat, lon) 元组 lat1, lon1 start lat2, lon2 end steps int(duration_sec / interval) route [] for i in range(steps 1): t i / steps lat lat1 (lat2 - lat1) * t lon lon1 (lon2 - lon1) * t route.append((lat, lon)) return route # 用法从 A 点跑到 B 点30 分钟 route generate_route((39.9042, 116.4074), (39.9142, 116.4174), 1800) for lat, lon in route: # 这里调用上面的 inject_point # await inject_point(lat, lon) time.sleep(1.0) # 必须加延时否则设备端会丢包参数说明duration_sec是总时长interval是注入间隔默认 1 秒。steps是总点数。注意time.sleep不能省我试过不加延时跑 200 个点之后设备直接断开连接血泪经验。3. 跨平台工具链搭建Windows 与 macOS 的依赖差异3.1 Windows 上的 usbmux 驱动与 Python 环境Windows 上最大的坑是 usbmux 服务。iTunes 会装一个 Apple Mobile Device Service但pymobiledevice3需要的是 usbmuxd 的 Windows 移植版。常见做法是装一个usbmuxd的独立包或者直接用pymobiledevice3自带的usbmux后端。我一般会先跑pymobiledevice3 usbmux list看能不能识别设备。如果报错No device found先检查 Apple Mobile Device Service 是否启动再检查设备是否信任了这台电脑。Python 版本建议 3.10 以上3.9 在异步部分会有兼容问题。# Windows 上安装依赖 pip install pymobiledevice3 # 启动 usbmux 服务如果没装 iTunes pymobiledevice3 usbmux start # 列出设备确认连接 pymobiledevice3 usbmux list逻辑说明usbmux start会在后台起一个服务监听 USB 连接。list会输出设备 UDID 和连接类型。如果这里能看到设备后面的注入基本不会有大问题。参数方面--udid可以指定设备多设备时必加。3.2 macOS 上的权限与开发者模式确认macOS 上相对简单系统自带 usbmuxd不需要额外装驱动。但 macOS 的隐私权限会拦一道终端或者你的 IDE 需要“输入监控”权限否则pymobiledevice3无法建立调试会话。这个权限在系统设置-隐私与安全性-输入监控里勾选。另外iOS 17 的设备在 macOS 上第一次连接时必须在设备上点“信任”然后去设置里打开开发者模式。开发者模式打开后设备会重启一次重启后确认即可。# macOS 上确认设备已连接 pymobiledevice3 usbmux list # 如果报权限错误去系统设置里给终端开输入监控 # 然后跑一个测试注入 pymobiledevice3 developer dvt simulate-location set --lat 39.9042 --lon 116.4074参数说明--lat和--lon是十进制度simulate-location set是子命令。这条命令跑通说明整条链路没问题。注意 macOS 上如果用了 zsh路径里的空格要转义。3.3 路线文件格式与批量注入脚本实际跑跑步模拟不可能手动敲坐标。我一般会把路线存成 GPX 或者 CSV。GPX 是跑步 App 通用的格式但注入时只需要经纬度和时间戳所以 CSV 更轻。下面是一个批量注入的脚本框架读 CSV按时间戳插值然后逐点注入。import csv import asyncio from pymobiledevice3.lockdown import create_using_usbmux from pymobiledevice3.services.dvt.instruments.dvt_provider import DvtProvider from pymobiledevice3.services.dvt.instruments.location_simulation import LocationSimulation async def run_route(csv_path): points [] with open(csv_path, r) as f: reader csv.DictReader(f) for row in reader: points.append((float(row[lat]), float(row[lon]), float(row[t]))) lockdown await create_using_usbmux() async with DvtProvider(lockdown) as dvt: async with LocationSimulation(dvt) as sim: for lat, lon, t in points: await sim.set(lat, lon) await asyncio.sleep(t) # t 是相对上一个点的时间差 # CSV 格式lat,lon,t # 39.9042,116.4074,1.0 # 39.9043,116.4075,1.0逻辑说明t列是相对时间差单位秒。asyncio.sleep保证注入节奏和真实跑步一致。参数方面CSV 的列名必须和脚本里一致大小写敏感。如果路线很长建议分段跑每 10 分钟停一次让设备喘口气。4. 避坑与排查那些让模拟直接失效的细节4.1 现象注入成功但 App 读不到坐标原因跑步 App 可能用了独立的定位模块比如某些 App 会校验CLLocation的sourceInformation字段。如果 App 发现来源是调试注入会直接忽略。解决换一个不校验来源的 App 测试或者用 Xcode 的模拟定位先验证。常见做法是先用系统地图验证注入是否生效地图能跟着动说明链路没问题问题在 App 侧。4.2 现象跑几分钟后设备断开连接原因USB 供电不足或者注入频率太高。iOS 设备在调试模式下功耗比平时高如果用的是劣质线或者 Hub很容易掉。解决直连电脑 USB 口别用 Hub。注入间隔从 1 秒改成 2 秒减少设备端压力。我试过连续注入 3000 个点中间断了三次后来改成每 500 个点停 5 秒稳了。4.3 现象坐标漂移轨迹画出来是锯齿原因插值算法太粗糙或者时间戳没对齐。如果 App 按 1Hz 采样你注入的点间隔不均匀就会算出锯齿速度。解决固定注入间隔比如严格 1 秒一个点。另外纬度经度的精度保留 6 位小数就够了太多位反而引入浮点误差。4.4 现象iOS 17.4 之后开发者模式开关消失原因系统更新后开发者模式开关有时会隐藏需要重新插拔一次电脑才会出现。解决拔掉设备重新插上去设置-隐私与安全性里找。如果还是没有重启设备再插。这个坑我踩过两次一度以为设备坏了。4.5 现象Windows 上usbmux list一直为空原因Apple Mobile Device Service 没启动或者被安全软件拦了。解决去服务里手动启动或者重装 iTunes 的驱动部分。另外Windows 11 的 USB 选择性暂停也会导致设备掉线去电源选项里关掉。5. 进阶用速度曲线让轨迹更像真人5.1 为什么匀速跑会被判定异常真人跑步不可能全程匀速。起跑慢、中途有波动、遇到路口会减速。如果注入的坐标速度恒定App 的风控模型很容易标记。我一般会加一个速度扰动函数用正弦波叠加随机噪声让配速在 ±10% 内波动。下面是一个生成速度曲线的片段import math import random def speed_profile(base_speed, duration_sec, interval1.0): # base_speed 单位米/秒 speeds [] for i in range(int(duration_sec / interval)): t i * interval # 正弦波动 随机噪声 wave math.sin(t / 60.0) * 0.1 noise random.uniform(-0.05, 0.05) speed base_speed * (1 wave noise) speeds.append(speed) return speeds # 用法配速 6 分钟每公里即 2.78 米/秒 speeds speed_profile(2.78, 1800)参数说明base_speed是基准速度wave是周期 60 秒的正弦波幅度 10%。noise是 ±5% 的随机噪声。这样生成的配速曲线更接近真人。注意速度变化不能太剧烈否则 App 会判定为异常加速。5.2 用 GPX 文件做路线回放如果你有真实的跑步 GPX 文件可以直接回放。GPX 里有时间戳和坐标解析后按时间差注入即可。常见做法是用gpxpy库解析然后转成上面的 CSV 格式。这样连速度曲线都不用自己生成直接用真实数据。import gpxpy def gpx_to_csv(gpx_path, csv_path): with open(gpx_path, r) as f: gpx gpxpy.parse(f) points [] for track in gpx.tracks: for segment in track.segments: for point in segment.points: points.append((point.latitude, point.longitude, point.time)) # 写入 CSV计算时间差 with open(csv_path, w) as f: f.write(lat,lon,t\n) for i in range(1, len(points)): lat, lon, t points[i] prev_t points[i-1][2] delta (t - prev_t).total_seconds() f.write(f{lat},{lon},{delta}\n)逻辑说明gpxpy解析后拿到每个点的经纬度和时间。delta是相邻点的时间差单位秒。写入 CSV 后直接用上面的批量注入脚本跑。参数方面GPX 文件必须是有效的 XML时间戳必须是 UTC。如果时间戳缺失脚本会报错。5.3 验证注入是否生效的三种方法第一种打开系统地图看蓝点是否跟着动。第二种用pymobiledevice3的simulate-location get读当前注入值。第三种在跑步 App 里看轨迹回放如果轨迹和预期一致说明整条链路通了。我一般会先跑一个 100 米的短路线确认没问题再跑长路线。从那以后我每次换设备或者升级系统都强制走一遍短路线验证省得跑到一半翻车。希望帮到你。本文还有配套的精品资源点击获取