release: PaopaoLocationSpoofer v1.0.0

- iOS 虚拟定位工具,基于本地 HTTP 代理 MITM 方案
- MapKit 原生地图体验,支持搜索、收藏、实时定位
- 完整的设置引导流程(证书安装、WiFi 代理配置、环境验证)
- 支持 iOS 15+,SwiftUI 构建
This commit is contained in:
xweiba
2026-08-05 15:45:24 +08:00
commit c8aba2be78
75 changed files with 7339 additions and 0 deletions
+234
View File
@@ -0,0 +1,234 @@
import Foundation
import SwiftUI
@MainActor
final class SetupCoordinator: ObservableObject {
@Published private(set) var trustState: CertificateTrustState = .checking
@Published var message = ""
@Published var isBrowsingWithoutTrust = false
@Published var testLog = ""
// false
@Published var needsSetup = false
let certificateStore = CertificateAuthorityStore()
let proxy = ProxyManager.shared
private var isVerificationRunning = false
init() { RuntimeLogger.info("APP", "Setup", "初始化") }
private var appVersion: String {
let v = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "?"
let b = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "?"
return "\(v) (\(b))"
}
var canModify: Bool { proxy.isRunning && trustState == .trusted }
func refreshTrust() async {
trustState = .checking
message = "正在检测…"
testLog = ""
do {
_ = try certificateStore.ensure()
if !proxy.isRunning { try await proxy.start() }
// POST Apple TLS = CA
let config = URLSessionConfiguration.ephemeral
config.timeoutIntervalForRequest = 5
config.timeoutIntervalForResource = 8
config.connectionProxyDictionary = [
kCFNetworkProxiesHTTPEnable as String: true,
kCFNetworkProxiesHTTPProxy as String: "127.0.0.1",
kCFNetworkProxiesHTTPPort as String: 8888,
]
//
let saved = WlocSettingsStore.load()
let testLat = saved?.latitude ?? 22.543099
let testLon = saved?.longitude ?? 113.934576
let testAccuracy = saved?.accuracy ?? 25
let req = makeWlocRequest()
let (_, resp) = try await URLSession(configuration: config).data(for: req)
let status = (resp as? HTTPURLResponse)?.statusCode ?? 0
// 400 = Apple TLS =
if status == 0 {
trustState = .unavailable
message = "代理链路异常,未收到响应"
return
}
// Go Core Apple
let patchResult = CoreBridge.testWlocPatch(lat: testLat, lon: testLon, accuracy: testAccuracy)
if patchResult.hasPrefix("ok:") {
trustState = .trusted
message = "✓ 定位环境正常(返回 \(status)"
} else {
trustState = .unavailable
message = "定位数据改写验证失败:\(patchResult)"
}
} catch {
trustState = .unavailable
let ns = error as NSError
if ns.domain == NSURLErrorDomain && ns.code == -1202 {
message = "CA 证书未信任,请去「设置→通用→关于→证书信任设置」开启或重新安装"
} else if ns.domain == NSURLErrorDomain && ns.code == -1001 {
message = "检测超时,请检查代理是否正常"
} else if ns.domain == NSURLErrorDomain && ns.code == -1200 {
message = "代理未启动或无法连接"
} else {
message = "检测失败 [\(ns.domain) \(ns.code)]: \(ns.localizedDescription)"
}
}
needsSetup = !canModify
}
func sceneDidBecomeActive() {}
func browseMapWithoutSetup() { isBrowsingWithoutTrust = true; needsSetup = false }
func completeSetup() { needsSetup = false }
func requestSetup() { needsSetup = true }
// MARK: - Step-by-step verification test
private func makeWlocRequest() -> URLRequest {
var req = URLRequest(url: URL(string: "https://gs-loc.apple.com/clls/wloc")!)
req.httpMethod = "POST"
req.httpBody = CoreBridge.testWlocRequestData()
req.setValue("application/x-protobuf", forHTTPHeaderField: "Content-Type")
req.setValue("wloc/1.0", forHTTPHeaderField: "User-Agent")
req.setValue("application/x-protobuf", forHTTPHeaderField: "Accept")
return req
}
func runVerificationTest(testLat: Double = 22.543099, testLon: Double = 113.934576) async -> VerificationResult {
guard !isVerificationRunning else { return .verificationInProgress }
isVerificationRunning = true
defer { isVerificationRunning = false }
testLog = ""
let log = { (msg: String) in self.testLog += msg + "\n" }
log("======== 代理验证测试 ========")
log("App 版本: \(appVersion)")
log("系统版本: iOS \(UIDevice.current.systemVersion)")
log("测试目标: lat=\(testLat), lon=\(testLon)")
log("")
// Step A: Proxy running
log("[步骤 A] 检查代理是否运行…")
log(" 端口: 127.0.0.1:8888")
let stepAStart = Date()
if !proxy.isRunning {
log(" ⚠ 代理未运行,尝试启动…")
do { try await proxy.start() } catch {
log(" ✗ 启动失败: \(error.localizedDescription)")
return .proxyNotRunning
}
log(" ✓ 代理启动成功")
} else {
log(" ✓ 代理已在运行中")
}
collectProxyLogs(since: stepAStart, to: log)
// Verification must never overwrite a newer location action.
let previousCoordinates = proxy.coordinateSnapshot(
accuracy: WlocSettingsStore.load()?.accuracy ?? 25
)
var verificationCoordinateRevision: UInt64?
defer {
if let revision = verificationCoordinateRevision {
let restored = proxy.restoreCoords(previousCoordinates, ifUnchangedSince: revision)
log(restored ? " ↩ 已恢复验证前的代理坐标" : " ↩ 检测到更新位置,跳过旧坐标恢复")
}
}
// Step B: Combined CA + WiFi proxy check (single request)
log("")
log("[步骤 B] 检测证书与 WiFi 代理…")
log(" 方式: 请求 baidu.com/paopao-verify-<token>")
log(" 结果判定: TLS 错误=证书问题 / 响应不匹配=代理未配置 / 匹配=通过")
let stepBStart = Date()
let verifyToken = CoreBridge.refreshVerifyToken()
guard !verifyToken.isEmpty else {
log(" ✗ 无法生成验证 token")
return .certNotTrusted
}
do {
let url = URL(string: "https://www.baidu.com/paopao-verify-\(verifyToken)")!
var req = URLRequest(url: url)
req.timeoutInterval = 8
req.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData
let config = URLSessionConfiguration.ephemeral
let (data, resp) = try await URLSession(configuration: config).data(for: req)
let statusCode = (resp as? HTTPURLResponse)?.statusCode ?? 0
let body = String(data: data, encoding: .utf8) ?? ""
if body == verifyToken {
log(" ✓ 证书已信任,WiFi 代理已配置")
} else {
log(" ✗ 响应不匹配 (HTTP \(statusCode))WiFi 代理未配置")
log(" 收到: \(body.prefix(100))")
return .wifiProxyNotConfigured
}
} catch {
let ns = error as NSError
let msg = error.localizedDescription
log(" ✗ 请求失败 [\(ns.domain) code=\(ns.code)]: \(msg)")
if ns.domain == NSURLErrorDomain && ns.code == -1202 {
log(" TLS 握手被拒,CA 证书未信任")
return .certNotTrusted
}
if msg.contains("TLS") {
log(" 包含 TLS → 证书问题")
return .certNotTrusted
}
return .wifiProxyNotConfigured
}
collectProxyLogs(since: stepBStart, to: log)
// Step C: Write and verify coordinates
log("[步骤 C] 写入测试坐标并验证…")
guard let testCoordinateRevision = proxy.setCoordsIfUnchanged(
lat: testLat,
lon: testLon,
enabled: true,
accuracy: 25,
expectedRevision: previousCoordinates.revision
) else {
log(" ↪ 检测到更新位置,取消过期验证坐标写入")
return .verificationSuperseded
}
verificationCoordinateRevision = testCoordinateRevision
let coords = proxy.getCoords()
if coords.enabled && abs(coords.lat - testLat) < 0.001 && abs(coords.lon - testLon) < 0.001 {
log(" ✓ 坐标写入成功: lat=\(coords.lat) lon=\(coords.lon)")
} else {
log(" ✗ 坐标验证失败: enabled=\(coords.enabled) lat=\(coords.lat) lon=\(coords.lon)")
return .coordinateWriteFailed("写入后回读不一致")
}
// Step D: verify data rewriting via Go test patch
log("[步骤 D] 验证定位数据改写…")
let result = CoreBridge.testWlocPatch(lat: testLat, lon: testLon, accuracy: 25)
log(" \(result)")
if result.hasPrefix("ok:") {
log(" ✓ 定位数据改写验证通过")
} else {
log(" ✗ 定位数据改写失败")
return .patchFailed(result)
}
log("")
log("======== 环境检测通过 ✓ ========")
return .success
}
/// Go CONNECT/// testLog
private func collectProxyLogs(since date: Date, to log: (String) -> Void) {
CoreBridge.flushLogs(category: "Proxy")
let entries = RuntimeLogStore.loadAll(limit: 200)
let proxyEntries = entries.filter {
$0.source == "CORE" && $0.category == "Proxy" && $0.timestamp >= date
}
guard !proxyEntries.isEmpty else { return }
log(" --- 代理日志 ---")
for e in proxyEntries {
log(" " + e.message)
}
}
}