我已经使用 NEPacketTunnelProvider 和网络扩展发布了一个Beta Proxyman iOS(网站) - 一个网络嗅探器,所以我可能有经验回答你的一些问题。
IP包,IP图,DNS,如何解析?
幸运的是,还有另一种方法可以设置 NEPacketTunnelProvider 来为您提供 HTTP 消息,而不是 IP 包(它太低级了,您必须处理 Parser、DNS ......)
HTTP 消息更容易解析,因为有很多可靠的库(例如来自 nodeJS的 http-parser )
- 如何在 iOS 上构建网络嗅探器?
这是一个复杂的问题,我会把它分成小块:
中间人/代理服务器
首先,您需要一个可以工作的 MitM 代理服务器,它能够代理和拦截 HTTP/HTTPS 流量。您可以使用SwiftNIO或CocoaAsyncSocket来实现它。
它是如何工作的?
通常,数据流可能如下所示:
Internet -> iPhone -> 您的网络扩展 (VPN) -> 转发到您的本地代理服务器(在网络扩展中)-> Mitm/代理服务器开始拦截或监控流量 -> 保存到本地数据库(在共享容器中组)-> 再次转发到目标服务器。
在主应用程序中,您可以通过读取本地数据库来接收数据。
我们需要本地数据库的原因是网络扩展和主应用程序是两个不同的进程,因此它们不能像普通应用程序那样直接通信。
给我看代码?
在 Network 扩展中,让我们在 Host:Port 启动一个代理服务器,然后初始化 NetworkSetting,如示例:
private func initTunnelSettings(proxyHost: String, proxyPort: Int) -> NEPacketTunnelNetworkSettings {
let settings: NEPacketTunnelNetworkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
/* proxy settings */
let proxySettings: NEProxySettings = NEProxySettings()
proxySettings.httpServer = NEProxyServer(
address: proxyHost,
port: proxyPort
)
proxySettings.httpsServer = NEProxyServer(
address: proxyHost,
port: proxyPort
)
proxySettings.autoProxyConfigurationEnabled = false
proxySettings.httpEnabled = true
proxySettings.httpsEnabled = true
proxySettings.excludeSimpleHostnames = true
proxySettings.exceptionList = [
"192.168.0.0/16",
"10.0.0.0/8",
"172.16.0.0/12",
"127.0.0.1",
"localhost",
"*.local"
]
settings.proxySettings = proxySettings
/* ipv4 settings */
let ipv4Settings: NEIPv4Settings = NEIPv4Settings(
addresses: [settings.tunnelRemoteAddress],
subnetMasks: ["255.255.255.255"]
)
ipv4Settings.includedRoutes = [NEIPv4Route.default()]
ipv4Settings.excludedRoutes = [
NEIPv4Route(destinationAddress: "192.168.0.0", subnetMask: "255.255.0.0"),
NEIPv4Route(destinationAddress: "10.0.0.0", subnetMask: "255.0.0.0"),
NEIPv4Route(destinationAddress: "172.16.0.0", subnetMask: "255.240.0.0")
]
settings.ipv4Settings = ipv4Settings
/* MTU */
settings.mtu = 1500
return settings
}
然后启动VPN,
let networkSettings = initTunnelSettings(proxyHost: ip, proxyPort: port)
// Start
setTunnelNetworkSettings(networkSettings) { // Handle success }
然后将包转发到您的本地代理服务器:
let endpoint = NWHostEndpoint(hostname: proxyIP, port: proxyPort)
self.connection = self.createTCPConnection(to: endpoint, enableTLS: false, tlsParameters: nil, delegate: nil)
packetFlow.readPackets {[weak self] (packets, protocols) in
guard let strongSelf = self else { return }
for packet in packets {
strongSelf.connection.write(packet, completionHandler: { (error) in
})
}
// Repeat
strongSelf.readPackets()
}
从那里,您的本地服务器可以接收包然后转发到目标服务器。
不要忘记将所有流量日志保存到本地数据库,然后通知主应用重新加载它。
最后一个问题:Charles 代理在大多数情况下能够显示目标的主机名。我目前只能看到目标 IP 地址(不是真正的目标 IP 地址,而是我的 DNS 服务器的地址)。我如何能够将主机名视为人类可读的文本?查尔斯会以某种方式进行 nslookup 吗?Charles 是否从数据报中获取这些信息?
由于我们不处理 IP Package,因此我们不需要实现 DNS Resolver。如果你需要一个 DNS,你可以像下面的代码一样配置:
let dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "1.1.1.1"])
settings.dnsSettings = dnsSettings
当我们收到 HTTP 消息包时,您可以免费获取主机名(来自请求的 URL 或主机头)
希望我的回答能帮到你。