iOS无VPN图标抓包技术详解
引言
在iOS应用开发和网络调试过程中,抓包是一项不可或缺的技术手段。然而,传统的抓包方式通常基于VPN或代理,这会在状态栏显示VPN图标,且需要设置WiFi代理或安装额外证书。本文将详细介绍一种基于iOS的NetworkExtension框架的ContentFilter技术,实现无VPN图标的网络抓包方案,同时保留获取应用标识(BundleID)和修改流量的能力。
技术背景
在深入探讨具体实现方案前,我们首先了解iOS中几种不同的网络扩展机制:
1. PacketTunnel (NEPacketTunnelProvider)
- 工作层级:IP层,处理原始IP数据包
- 优势:可捕获所有网络流量
- 局限性:无法识别流量来源的应用(无BundleID信息)
- 状态栏显示:会显示VPN图标
2. AppProxy (NEAppProxyProvider)
- 工作层级:TCP/UDP层
- 优势:可获取流量对应的应用信息
- 局限性:需要预先配置目标应用
- 状态栏显示:会显示VPN图标
3. ContentFilter (NEFilterProvider)
- 工作层级:应用层
- 组成部分:
- NEFilterDataProvider:获取流量内容,但处于严格沙盒
- NEFilterControlProvider:获取元数据,不受严格沙盒限制
- 优势:不显示VPN图标,可获取流量对应的应用信息
- 局限性:默认只能允许或阻断流量,不能直接修改
核心原理解析
我们的解决方案基于ContentFilter与其他技术的巧妙组合,核心原理如下:
-
利用NEFilterDataProvider获取流量源信息
- 获取数据包及对应的BundleID
- 由于处于严格沙盒,无法直接传出数据
-
巧妙触发NEFilterControlProvider
- 让NEFilterDataProvider总是返回
needRules
状态 - 系统会自动调用对应的NEFilterControlProvider
- 让NEFilterDataProvider总是返回
-
通过NEFilterControlProvider传递元数据
- 获取IP、端口、BundleID等关键信息
- 将这些信息传递给其他模块(如主应用或PacketTunnel)
-
可选:结合PacketTunnel实现流量修改
- ContentFilter负责识别流量来源
- PacketTunnel负责实际的流量处理和修改
详细实现步骤
下面将详细介绍从零开始实现这一技术的完整流程:
前期准备
必备条件
- 苹果开发者账号(付费,每年99美元)
- 个人免费开发者账号权限不足,无法使用所需的NetworkExtension特殊权限
- 最新版Xcode
- macOS系统电脑
- iOS测试设备(不能在模拟器上测试网络扩展功能)
步骤1:创建应用并配置证书
1.1 创建App ID
- 登录Apple Developer Portal
- 导航至"Certificates, Identifiers & Profiles"
- 选择"Identifiers"并点击"+"创建新的App ID
- 填写描述和Bundle Identifier(例如:com.yourcompany.NetworkDebugger)
- 在Capabilities部分,启用以下服务:
- Network Extensions
- App Groups
1.2 创建App Group
- 在"Certificates, Identifiers & Profiles"中选择"App Groups"
- 点击"+"创建新的App Group
- 设置标识符(例如:group.com.yourcompany.NetworkDebugger)
- 填写描述并保存
1.3 创建Provisioning Profile
- 在"Certificates, Identifiers & Profiles"中选择"Profiles"
- 点击"+"创建新的Provisioning Profile
- 选择"iOS App Development"
- 选择之前创建的App ID
- 选择开发证书
- 选择测试设备
- 输入Profile名称并生成
- 下载并双击安装到Xcode
步骤2:创建基础项目
2.1 创建新的Xcode项目
- 打开Xcode,选择"Create a new Xcode project"
- 选择"App"模板
- 填写项目信息:
- Product Name: NetworkDebugger
- Organization Identifier: com.yourcompany
- Bundle Identifier: com.yourcompany.NetworkDebugger
- Interface: SwiftUI或Storyboard(根据个人偏好)
- Language: Swift
2.2 配置项目能力
- 选择项目 -> Targets -> NetworkDebugger -> Signing & Capabilities
- 确保开发团队已设置为付费开发者账号
- 点击"+ Capability"按钮,添加以下能力:
- Network Extensions
- App Groups
- 在App Groups部分,添加之前创建的App Group
步骤3:添加Network Extension Target
3.1 添加Content Filter Extension
- 在Xcode中,选择File -> New -> Target
- 选择"Network Extension"类别
- 选择"Content Filter Provider"模板
- 填写信息:
- Product Name: ContentFilterExtension
- Organization Identifier: com.yourcompany
- Bundle Identifier将自动填充
- Language: Swift
- 确保"Include Network Extension Infrastructure"选项已勾选
- 点击"Finish"创建
3.2 配置Content Filter Extension的能力
- 选择项目 -> Targets -> ContentFilterExtension -> Signing & Capabilities
- 确保已添加以下能力:
- Network Extensions
- App Groups(添加与主应用相同的App Group)
- 在Network Extensions部分,确保已选中Content Filter Provider
3.3 添加Packet Tunnel Provider(可选,用于修改流量)
- 再次添加新Target,选择File -> New -> Target
- 选择"Network Extension"类别
- 选择"Packet Tunnel Provider"模板
- 填写信息:
- Product Name: PacketTunnelExtension
- Organization Identifier: com.yourcompany
- Language: Swift
- 点击"Finish"创建
3.4 配置Packet Tunnel Extension的能力
- 选择项目 -> Targets -> PacketTunnelExtension -> Signing & Capabilities
- 添加以下能力:
- Network Extensions
- App Groups(添加与主应用相同的App Group)
- 在Network Extensions部分,确保已选中Packet Tunnel Provider
步骤4:配置Content Filter的核心代码
4.1 实现NEFilterDataProvider类
打开并编辑ContentFilterProvider.swift文件:
import NetworkExtensionclass ContentFilterProvider: NEFilterDataProvider {// 过滤器启动override func startFilter(completionHandler: @escaping (Error?) -> Void) {// 初始化日志NSLog("ContentFilterProvider: startFilter")// 完成启动completionHandler(nil)}// 过滤器停止override func stopFilter(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {NSLog("ContentFilterProvider: stopFilter with reason \(reason.rawValue)")// 完成停止completionHandler()}// 处理新的流量override func handleNewFlow(_ flow: NEFilterFlow) -> NEFilterNewFlowVerdict {// 关键点:总是返回needRules,这样会触发NEFilterControlProviderreturn .needRules()}
}
4.2 创建NEFilterControlProvider类
新建一个文件FilterControlProvider.swift:
import NetworkExtensionclass FilterControlProvider: NEFilterControlProvider {// 处理流量修复决策override func handleRemediationForFlow(_ flow: NEFilterFlow, verdictDelayed: Bool, remedyURLMapKeys: [URLResourceKey]?, completionHandler: @escaping (NEFilterControlVerdict) -> Void) {// 提取流量信息if let socketFlow = flow as? NEFilterSocketFlow {// 获取远程端点信息let remoteEndpoint = socketFlow.remoteEndpointlet localEndpoint = socketFlow.localEndpoint// 获取应用标识符let bundleID = socketFlow.sourceAppIdentifier ?? "Unknown"let direction = socketFlow.direction// 构造流量信息对象let flowInfo: [String: Any] = ["bundleID": bundleID,"remoteHost": remoteEndpoint?.hostname ?? "unknown","remotePort": remoteEndpoint?.port ?? 0,"localPort": localEndpoint?.port ?? 0,"direction": direction == .outbound ? "outbound" : "inbound","timestamp": Date().timeIntervalSince1970]// 将信息写入共享存储self.saveFlowInfo(flowInfo)NSLog