ios - NEPacketTunnelProvider 嗅探器 iOS

标签 ios swift datagram sniffing networkextension

正如我最近发现的 paper描述 iOS 的嗅探机制使用苹果的 NEPacketTunnelProvider扩展,我很好奇,这让我想从技术角度理解它。由于我通常不在这样的深层网络层工作,因此我无法详细理解它。如Charles Proxy因为 iOS 必须在不需要受监督设备的情况下做一些非常相似的事情,我认为作者在 2016 年提出的方法现在可能仍然有效。

作者声称“一切都像 IP 数据包解析、构建
IP 数据包或解析 DNS 响应必须自己实现。” 因为我想完全理解这一点,所以我尝试自己构建它。我构建了 NetworkExtensionpacketFlowNEPacketTunnelProvider 的消息循环.我能够获取ip数据报并尝试解析它们.我使用源和目标ip、传输协议(protocol)和ip版本对应大小的无符号整数,但我不确定如何处理payload。我的解析器使用 ptr.load(fromByteOffset: <offset>, as:<DataType>.self) 其中 ptrUnsafeRawPointer 来访问数据包流信息。由于 data 可能超过 UInt64 的存储,我不知道如何访问和存储有效负载一个适当的方法。

此外,我认为源 IP 始终是 192.168.20.1 (设置为我接口(interface)的NEIPv4Settings 地址),我的目标IP 始终是192.168.2.1 (我的虚拟 NEDNSSettings 服务器)。这引出了我的第一个问题:这些是 DNS 查询吗?数据报包会要求有关实际目标的任何进一步信息吗?这是否意味着我必须以某种方式执行对 DNS 服务器的请求并将数据包重新路由到我将从该 DNS 查询中获得的目标?

下一步将是实现 TCP/UDP 处理,对吧?我目前的解析方法能够区分 UDP , TCPICMP (尽管我还没有在最后一个调查过)。因此,我将遍历数据报并查找它们是否需要 UPDTCP session /连接并传输数据报。我目前从概念的角度看到的问题:我如何知道哪个源/目标端口用于 TCP/UPD 连接/ session ?据我所知,这些信息不是 IP 数据包本身的一部分(因为它是我们需要的传输层级别的一些信息,而不是网络层级别的信息)。

此外,我发现了一个名为 Specht 的项目。在github上。它使用了一个名为 NEKit 的自写库。不知何故也使用了NEPacketTunnelProvider方法。当我正确理解他们的方法时,他们设法通过编写一些观察者机制来构建本地代理服务器以处理请求,但由于我对网络和 swift 比较陌生,我不确定我是否完全理解正确还是我只是没有找到所有那些 TCP/UDP 和/或 DNS 逻辑。这个项目可以与论文和查尔斯代理的方法相媲美吗?

最后一个问题:Charles 代理在大多数情况下能够显示目标的主机名。我目前只能看到目标 IP 地址(不是真正的目标 IP 地址,而是我的 DNS 服务器的地址)。我如何能够将主机名视为人类可读的文本?查尔斯做一个nslookup不知何故? Charles 是否从数据报中获取这些信息?

我知道我很有野心,因为我在这个主题上缺少很多知识,为了测试原因构建类似的东西,但我仍然有动力去更深入地研究这个主题,并且感觉我已经理解了一些关键点,但是不幸的是,还不足以解决这个难题……也许您可以给我更多提示以更好地理解。如果可能有更简单的方法来存档类似的行为(查看主机名级别的传出连接),我也会对这些感兴趣:-)

最佳答案

我已经发布了 Beta Proxyman iOS ( website ) - 一个使用 NEPacketTunnelProvider 和网络扩展的网络嗅探器,所以我可能有经验回答你的一些问题。

IP Package, IP Diagram, DNS, How to parse it?


幸运的是,还有另一种方法可以设置 NEPacketTunnelProvider 来为您提供 HTTP 消息,而不是 IP 包(它太低级了,您必须处理 Parser、DNS ......)
HTTP 消息更容易解析,因为有很多可靠的库(例如来自 nodeJS 的 http-parser)
  1. How to build a Network Sniffer on iOS?

这是一个复杂的问题,我会把它分成小块:

MitM / Proxy Server


首先,您需要一个可以工作的 MitM 代理服务器,它能够代理和拦截 HTTP/HTTPS 流量。您可以使用 SwiftNIO 来实现它或 CocoaAsyncSocket .

How does it work?


通常,数据流可能如下所示:
Internet -> iPhone -> 您的网络扩展 (VPN) -> 转发到您的本地代理服务器(在网络扩展中)-> Mitm/代理服务器开始拦截或监控流量 -> 保存到本地数据库(在共享容器中组)-> 再次转发到目标服务器。
在主应用程序中,您可以通过读取本地数据库来接收数据。
我们需要本地数据库的原因是网络扩展和主应用程序是两个不同的进程,因此它们不能像普通应用程序那样直接通信。

Show me the code?


在 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()
    }
从那里,您的本地服务器可以接收包然后转发到目标服务器。
不要忘记将所有流量日志保存到本地数据库,然后通知主应用重新加载它。

One last question: Charles proxy is in most cases able to show the hostname of the target. I'm currently just able to see destination ip addresses (which aren't real destination ip addresses, but the address of my DNS server). How am I able to see the hostname as human readable text? Does Charles do a nslookup somehow? Does Charles obtain that information out of the datagrams?


由于我们不处理 IP Package,因此我们不需要实现 DNS Resolver。如果你需要一个 DNS,你可以像下面的代码一样配置:
        let dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "1.1.1.1"])
    settings.dnsSettings = dnsSettings
当我们收到 HTTP 消息包时,您可以免费获取主机名(来自请求的 URL 或主机头)
希望我的回答能帮到你。

关于ios - NEPacketTunnelProvider 嗅探器 iOS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52476665/

相关文章:

iphone - 在 iOS 中添加应用程序范围标题 View 的最佳实践

ios - 如果使用 CKFetchRecordZoneChangesOperation 时应用程序被终止,会发生什么?

iOS 9 使用快速操作选择标签栏索引,performActionForShortcutItem

Java UDP 套接字回显字节

ios - 为什么 swift 在此处使用结构作为字典键而不是字符串?

ios - 在后台播放视频 swift 3

ios - UIScrollView下拉父 View

ios - 在 Realm 下载内容时如何更新 progressView

数据报 (UDP)winsocket 的 C++ 问题通过环回适配器在同一套接字上发送和接收

c - 为什么netcat收不到第二个广播报文?